Overview
Cap is a fun, but simple one. On the surface it looks like a straightforward web app, but it chains together three distinct concepts (IDOR, plaintext credential exposure, and Linux capability abuse) into a clean, satisfying path to root. Nothing here is overly complex, but it rewards curiosity and the habit of questioning what the app is actually doing under the hood.
Enumeration
As always, start by confirming the host is up via a ping, then kick off a basic scan:
nmap -sS -sV -O -Pn 10.129.1.88
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2
80/tcp open http gunicorn
Three ports. FTP on 21 catches the eye first: vsftpd 3.0.3 has a known Remote DoS floating around on ExploitDB, but that’s not going to help us get in. I also tested anonymous login, no luck. SSH on 22 is noted for later, as that could be a crucial pivoting point. The interesting one is port 80, running gunicorn, which is a Python WSGI server, so we’re likely looking at a Flask or Django app. Let’s go take a look.
Web Enumeration — IDOR
The web app is a security dashboard. Metrics up top, a sidebar with a few sections, one of which is Security Snapshots, where the app stores pcap files from network captures and lets you download them.
The URL for a snapshot looks like this:
http://10.129.1.88/data/1
That incrementing integer is immediately suspicious. Any time you see a number like that in a URL, it’s worth asking: what happens if I change it? Bumping it to /data/2 gives a different pcap. So does /data/3. The app is serving up packet captures indexed by ID with zero access control, textbook IDOR.
The obvious next move: what’s at /data/0?
http://10.129.1.88/data/0
Bingo. The captures at /data/1 and above were nearly empty. This one has 72 packets — something was actually captured here. Downloading it and cracking it open in Wireshark, we immediately see HTTP and FTP traffic mixed together.
FTP packets are always worth a closer look because FTP by default has no encryption whatsoever, so if any authentication happened over this connection, the credentials are sitting right there in the capture. And sure enough:
USER nathan
PASS Buck3tH4TF0RM3!
There they are, in plain text. That’s our entry point.
Initial Access
First let’s confirm the FTP credentials actually work:
ftp 10.129.1.88
# User: nathan
# Password: Buck3tH4TF0RM3!
# Login successful
Nice. Now, password reuse is one of those things that’s more common than it should be. Remember, SSH is open, so let’s try the same credentials there:
ssh nathan@10.129.1.88
# Password: Buck3tH4TF0RM3!
And we’re in. SSH session as nathan, user flag sitting right there in /home/nathan/user.txt. Now let’s see about getting root.
Privilege Escalation — Python cap_setuid
Time to enumerate. I dropped both Linux Exploit Suggester and LinPEAS onto the box via SCP:
git clone https://github.com/The-Z-Labs/linux-exploit-suggester
scp linux-exploit-suggester/linux-exploit-suggester.sh nathan@10.129.1.88:/home/nathan
wget https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh
scp linpeas.sh nathan@10.129.1.88:/home/nathan
I ran exploit suggestor and got some interesting results. Then I ran LinPEAS on the target:
chmod +x linpeas.sh && ./linpeas.sh
LinPEAS throws a lot at you, but one line stands out immediately:
/usr/bin/python3.8 = cap_setuid,cap_net_bind_service+eip
Python has cap_setuid. That’s a problem for the box owner, and a gift for us.
Quick explainer on Linux capabilities: they’re a way of granting a binary specific elevated privileges without making it fully SUID root. Instead of running something as root entirely, you can say “this binary is allowed to bind to low ports” or “this binary is allowed to change its user ID.” cap_setuid means the binary can call setuid() and switch to any UID it wants — including 0.
Python should absolutely not have this. But if it does, we can just ask Python to make us root:
/usr/bin/python3.8 -c 'import os; os.setuid(0); os.system("/bin/bash")'
Root shell. Just like that. Grab the flag from /root/root.txt and we’re done.
Summary
| Step | Technique |
|---|---|
| Enumeration | Nmap — FTP, SSH, HTTP identified |
| Foothold | IDOR on /data/0 → pcap → plaintext FTP creds |
| Initial Access | SSH with reused FTP credentials |
| Privilege Escalation | Python cap_setuid capability abuse |
Key Takeaways
- Always poke at URL parameters. An incrementing integer with no access control is IDOR waiting to happen — it takes five seconds to test and pays off constantly.
- FTP is plaintext by design. If you ever see FTP traffic in a pcap, read it. Credentials will often just be sitting there.
- Password reuse is everywhere. The moment you have valid creds for one service, try them on every other open port.
- Capabilities are an underrated privesc surface.
cap_setuidon any interpreter — Python, Perl, Ruby — is essentially SUID root. GTFOBins has you covered.