Busqueda — Searchor Expression Injection and Relative-Path Sudo Escalation
- Tools
- rustscan, python3, sudo, netcat
- Skill demonstrated
- Python expression injection, credential discovery, and Linux privilege escalation through a delegated maintenance script
- Tags
At a glance
Section titled “At a glance”| Field | Value |
|---|---|
| Difficulty | Easy |
| Target environment | Ubuntu Linux; Apache httpd 2.4.52 fronting a Python/Flask application built on Searchor |
| Starting position | Unauthenticated network access |
| Objective | Assess unsafe evaluation in a Flask/Searchor search request, credentials exposed in a deployment repository, and a root-run maintenance script that resolves a helper by relative path |
| Outcome | Command execution as the application service account; root command execution via a relative-path script invoked by a root-run maintenance command |
Summary
Section titled “Summary”Busqueda is an Easy Hack The Box Linux lab whose web front end runs a Flask search application built on Searchor. The query parameter reaches a Python eval() call, so a crafted search request runs operating-system commands as the application service account. Post-exploitation follows credentials left in the application’s Git configuration into an internal Gitea instance, inspects container environment variables through a delegated sudo maintenance script, and escalates to root by planting the helper that the script’s full-checkup action resolves by relative path. Target identifiers, account names, credential values, callback payloads, and private paths are replaced with role-based placeholders; command syntax is preserved.
Attack path: Searchor eval() injection → service-account shell → Git remote credential exposure → container environment secret disclosure via sudo docker-inspect → relative-path full-checkup helper → root
Context and Objective
Section titled “Context and Objective”- Target: an Ubuntu Linux host exposing SSH and an Apache-fronted HTTP application.
- Application: a Flask search service whose footer identifies the Searchor library, presented as a search-engine selector and a
queryfield; the site requires a virtual host mapping to reach. - Starting position: unauthenticated network access; directory enumeration returned little of interest.
- Objective: assess unsafe expression evaluation in the search request, credential exposure in the deployment repository, and the privilege boundary created by the allowed maintenance script.
- Constraints: activity was confined to the Hack The Box lab environment.
Approach and Evidence
Section titled “Approach and Evidence”1. Service Discovery
Section titled “1. Service Discovery”Observation: a full-port scan exposes SSH and a single HTTP service.
rustscan -a <TARGET_IP> --ulimit 5000 -- -Pn -sC -sV -oN <SCAN_OUTPUT>22/tcp open ssh OpenSSH 8.9p1 Ubuntu80/tcp open http Apache httpd 2.4.52Significance: HTTP is the only application-reachable service, and the search interface identifies a Python/Flask stack built on Searchor, so the query field is the initial attack surface; the SSH banner identifies the platform.
Result: SSH and Apache HTTP are exposed, and the Searchor-backed search application is the target surface.
2. Search Request Injection and Command Execution
Section titled “2. Search Request Injection and Command Execution”Observation: the query parameter is passed into a Python expression — ' and / characters change the response — and a crafted value breaks out of the expected string context to invoke an operating-system command.
POST /search HTTP/1.1Host: <TARGET_HOST>Content-Type: application/x-www-form-urlencoded
engine=Google&query=<PYTHON_EXPRESSION_INJECTION>The executed command returns a shell in the application directory:
<SERVICE_USER>@<TARGET_HOST>:<APPLICATION_DIRECTORY>$Significance: because the request value reaches eval(), search input becomes arbitrary code execution under the application service account. The defect is the Searchor eval() issue tracked as CVE-2023-43364, fixed in 2.4.2.
Result: a command channel as <SERVICE_USER> is established in the application directory.
3. Git Configuration Credential Exposure
Section titled “3. Git Configuration Credential Exposure”Observation: the application directory contains a readable .git directory whose remote URL embeds credentials.
cat <APPLICATION_DIRECTORY>/.git/configurl = http://<GIT_USER>:<GIT_PASSWORD>@<GITEA_HOST>/<OWNER>/<REPOSITORY>.gitThe internal services are bound to loopback, so the Gitea instance is not directly reachable:
ss -tulpn 2>/dev/null127.0.0.1:3000 Gitea127.0.0.1:3306 MySQLSignificance: a repository remote carries a credential pair for the internal Gitea service, and the loopback bindings show that service is reachable only through a tunnel.
Result: a Gitea credential pair is recovered from the repository configuration, and the internal services are identified.
4. Restricted Sudo Maintenance Script
Section titled “4. Restricted Sudo Maintenance Script”Observation: local sudo rights allow the service account to run one maintenance script as root with arbitrary trailing arguments.
sudo -l(root) /usr/bin/python3 <MAINTENANCE_SCRIPT> *Significance: the delegation is scoped to a single interpreter and script but accepts any argument, and the script exposes docker-ps, docker-inspect, and full-checkup actions — a root context offered through a constrained interface.
Result: a root-run maintenance script is reachable through the delegated sudo rule.
5. Container Environment Secret Disclosure
Section titled “5. Container Environment Secret Disclosure”Observation: the docker-ps action lists the running containers, and docker-inspect returns the environment of a chosen container.
sudo /usr/bin/python3 <MAINTENANCE_SCRIPT> docker-psgitea/gitea:latestmysql:8sudo /usr/bin/python3 <MAINTENANCE_SCRIPT> docker-inspect '{{.Config.Env}}' <DATABASE_CONTAINER>MYSQL_USER=<DATABASE_USER>MYSQL_PASSWORD=<DATABASE_PASSWORD>MYSQL_DATABASE=<DATABASE_NAME>Significance: the maintenance script returns raw container environment variables, disclosing the database credentials in cleartext — the application’s backend secret handed over through a permitted root action.
Result: Gitea database credentials are recovered from the container environment.
6. Relative-Path Execution as Root
Section titled “6. Relative-Path Execution as Root”Observation: the script’s full-checkup branch builds its command from a relative path, so the executable is resolved from the caller’s current directory rather than a fixed trusted location.
elif action == 'full-checkup': arg_list = ['./full-checkup.sh'] print(run_command(arg_list))Action — plant the named helper in the working directory and invoke the root sudo action from there:
nc -nlvp <LISTEN_PORT>printf '%s\n' '<SANITIZED_CALLBACK_PAYLOAD>' > full-checkup.shchmod +x full-checkup.shsudo /usr/bin/python3 <MAINTENANCE_SCRIPT> full-checkuproot@<TARGET_HOST>:<WORKING_DIRECTORY># whoamirootSignificance: because root runs ./full-checkup.sh from a caller-controlled working directory, a constrained sudo rule becomes arbitrary root code execution.
Result: root command execution is confirmed by the privileged whoami output.
Challenges and Decisions
Section titled “Challenges and Decisions”| Challenge | Decision | Rationale |
|---|---|---|
| Special characters in the injected command risked breaking the evaluated expression | Base64-encoded the reverse shell before sending it through the query parameter |
Avoids quoting and bad-character issues in the injected command |
Outcome
Section titled “Outcome”The lab ends with root command execution, established by the privileged whoami output. Two transitions are recorded without retained command output: the leaked Git password also authenticated the service account locally, and the disclosed database password granted Administrator access to the internal Gitea instance that held the maintenance script source.
Lessons and Recommendations
Section titled “Lessons and Recommendations”- User input evaluated as code. The search
queryreachedeval(), turning search input into command execution as the application account. Recommendation: remove dynamic evaluation of request data and use an allowlisted lookup, as Searchor 2.4.2 did. Detection: review application code foreval()/exec()on user input and monitor web processes for unexpected child processes. - Credentials in Git remote configuration. A deployment
.git/configembedded a credential pair for an internal repository host. Recommendation: use deploy keys or a credential helper instead of embedding secrets in remote URLs, and rotate any credential that has been exposed. Detection: scan working directories and repository configuration for credentials in remote URLs. - Secrets in container environment variables.
docker-inspectreturned the Gitea database password in cleartext. Recommendation: deliver secrets through a secret manager or mounted files rather than environment variables, and restrict who may inspect container configuration. Detection: alert on inspection of environment variables and configuration of production containers. - Relative-path execution in a root-run script. The
full-checkupbranch ran./full-checkup.shfrom the caller’s directory, so a delegated sudo rule became arbitrary root code execution. Recommendation: reference helper executables by absolute, root-owned paths and validate accepted arguments; avoid wildcard sudo rules that reach an interpreter. Detection: audit sudo policy for interpreter or wildcard delegations and alert on changes to scripts in privileged working directories.
References
Section titled “References”- Hack The Box — Busqueda (retired machine)
- NVD — CVE-2023-43364 (Searchor
eval()code execution) - GitHub Advisory — GHSA-66m2-493m-crh2 (Searchor vendor advisory)
- Searchor (search library)
- RustScan (fast port scanner)
- netcat (
nc) (network listener) - Python —
eval(dynamic expression evaluation) - sudoers manual (sudo policy and command delegation)