Busqueda — HackTheBox Machine Writeup
Port scan reveals SSH + HTTP. The web app is a search proxy powered by Flask and Searchor 2.4.0, whose eval()-based query builder is known to be injectable.
The POST /search endpoint splices the query into an eval() string. A popen() payload yields command execution as svc, which we use to leak credentials from the app's .git/config and pivot to SSH.
svc may run /opt/scripts/system-checkup.py as root. Its full-checkup action executes ./full-checkup.sh with a relative path, so a planted payload in the CWD runs as root and drops a setuid shell.
Challenge Overview
Busqueda is a clean, well-paced Easy box: a single vulnerable web library (Searchor 2.4.0) gives command execution; a leaked git remote URL inside the deployed app leaks credentials; and those credentials are reused for SSH and sudo, ultimately letting a relative-path invocation of a root-only script drop a setuid shell. No kernel tricks, no binary exploitation — just configuration mistakes and a famous eval() antipattern.
Beginner note: the whole box is a chain of "trust" mistakes — passing attacker input into eval(), committing credentials into a git remote URL, and reusing that same password everywhere. Keep a notepad of every credential you find; on this box they all interlock.
Enumeration & Web Application Recon
A full-port scan with service detection comes back clean:
Browsing to the IP redirects to http://searcher.htb/, so the target uses virtual hosts. Adding the box's hostnames to /etc/hosts:
The home page is a search form with a huge engine dropdown. Two details in the page stand out immediately:
- ▹The footer: "Powered by Flask and Searchor 2.4.0"
- ▹The
Server: Werkzeug/2.1.2 Python/3.10.6header
Searchor is a PyPI package that builds engine search URLs. Version 2.4.0 has a notorious flaw: the CLI and library build the search call via eval() on a format string, and both the engine and the query are interpolated unescaped.
Any version of Searchor before 2.4.2 is vulnerable. The patch (2.4.2) replaced the eval()-based URL builder with an explicit dictionary lookup — a textbook case of "eval is not a templating engine".
The Searchor 2.4.0 eval() Injection
We cover source code auditing techniques, dangerous dynamic code evaluation patterns (eval(), popen()), and parameter handling in full detail in our article: Source Code Review: Unearthing Critical Flaws in PHP.
The vulnerable code in src/searchor/main.py looks roughly like this:
The server-side application takes the user's query and passes it straight into that string. Because the query is wrapped in single quotes, a payload of the form
breaks out of the quoted string, evaluates as Python, and concatenates the command's stdout into the search URL. The app then renders that URL back in the response — which conveniently gives us a read channel for command output.
::Proof of Concept
The response is a GitHub search URL with our command output URL-encoded inside it:
Decoded: uid=1000(svc) gid=1000(svc) groups=1000(svc) — arbitrary command execution as the svc user.
Credential Harvesting via RCE
With a command channel, the first stop is the deployed app's own git history and configuration. The web root is /var/www/app:
The leaked .git/config contains a remote URL with embedded credentials:
A classic credential-in-URL leak. The password belongs to a gitea user cody. Rather than fighting the RCE for a stable shell, I tested the same password against SSH — and the box reused it for the svc account:
We cover local credential harvesting methodology, secrets extraction (.git/config, .env, process environments), and user enumeration in depth in our Linux Privilege Escalation: Enumeration Cheatsheet.
::User flag
Privilege Escalation — Relative-Path Hijack
::Checking sudo rights
svc can run one specific script as root — but only that exact interpreter invocation. The script itself is not readable (-rwx--x--x), so I pulled its source from the Gitea instance instead, using the administrator account on gitea.searcher.htb. The Gitea database password comes from inspecting the gitea docker container via the same sudo script:
That password logs into Gitea as administrator, and the administrator/scripts repo contains the source of system-checkup.py. The interesting branch:
The bug: ./full-checkup.sh is a relative path. The script never changes into /opt/scripts and never resolves the path against the script's own directory — it inherits the current working directory of whoever invokes it. So if I run sudo ... system-checkup.py full-checkup from a directory I control, the root process executes my full-checkup.sh.
This is the same class of bug as PATH hijacking, but for the working directory instead of PATH: the program trusts an environment-derived location (the CWD) instead of anchoring to an absolute path. Running sudo from /tmp is all it takes.
We cover relative PATH/CWD hijacking, sudo environment abuse, and GTFOBins setuid escapes fully in our Linux Privilege Escalation: Basics & Exploitation Cheatsheet.
::Crafting the payload
The sudo invocation ran from /tmp, so ./full-checkup.sh resolved to my payload, which ran as root and copied /bin/bash into a setuid binary. Executing it with preserved privileges gives an euid-0 shell:
Verification & Proof of Work
Two independent confirmations of the root shell:
- ▹File ownership check —
/tmp/bbshows-rwsr-xr-x 1 root root, proving the copy ran with root privileges (a non-root copy would show ownersvc). - ▹euid inspection —
idfrom inside the setuid shell reportseuid=0(root) egid=0(root)while the real uid stays1000; bash only honors the setuid bit with-pand reports it viaeuid, which is exactly what we observe.
::Root flag
Autosolve
The whole chain is scriptable end-to-end: the RCE gives us the credentials, the credentials give us SSH, and SSH gives us the sudo abuse. autosolve/htb_busqueda_solve.py runs it in six steps with no hardcoded secrets — everything is harvested off the live target.
Standalone Solver Script: View or run the complete automated solver
htb_busqueda_solve.py
in our /autosolve directory.
References

Red Team Consultant · Penetration Tester · Bug Bounty Hunter
Offensive security professional with 250+ vulnerabilities reported across 50+ organizations including Atlassian, Vimeo, and AT&T. Sharing research, tools, and field notes.