Back to Blog
HTB Write-up HackTheBox / Cap
HTB Write-up

IDOR to Root.
How Cap Punishes
Lazy Authorization.

A security dashboard that lets you view your own packet captures. Change one number in the URL and you can view everyone else's too. The PCAP from session zero holds FTP credentials in plaintext. SSH takes the same password. cap_setuid on Python 3.8 turns a standard user shell into root in a single line.


Machine Cap
Platform HackTheBox
OS Linux (Ubuntu 20.04)
Difficulty Easy
Date 29 Apr 2026
Status Rooted
Flags User + Root

The Machine

Cap is a Linux machine running a Python/Gunicorn-based security dashboard. The dashboard lets authenticated users run network captures and download the resulting PCAP files. The intended path is IDOR on the download endpoint, then credential extraction from the captured traffic, then Linux capability abuse for root.

It covers two of the most common real-world findings in web application assessments: insecure direct object references and plaintext protocol credential capture. The privilege escalation via Linux capabilities is a reminder to always run getcap as part of post-foothold enumeration. find -perm -4000 finds SUID bits. It does not find capabilities. They are a separate check.

PortServiceNotes
21FTP (vsftpd 3.0.3)Anonymous login disabled
22SSH (OpenSSH 8.2p1)Shell vector once credentials obtained
80HTTP (Gunicorn)Security dashboard. IDOR entry point

Enumeration

Two-phase nmap. Wide coverage first, then a targeted service scan on all open ports.

$ nmap -p- --min-rate 1000 -oN cap-all-ports.txt 10.129.26.15
Nmap wide port scan showing three open ports: 21, 22, 80 on Cap
$ ports=$(grep open cap-all-ports.txt | cut -d '/' -f1 | tr '\n' ',' | sed 's/,$//')
$ nmap -p $ports -sC -sV --min-rate 1000 -oN cap-service-scan.txt 10.129.26.15
Nmap service scan showing vsftpd 3.0.3 on 21, OpenSSH 8.2p1 on 22, Gunicorn HTTP on 80

Three open ports: FTP, SSH, and HTTP. FTP is the natural first test. Anonymous login is frequently enabled on HTB Linux machines.

$ ftp 10.129.26.15
FTP anonymous login rejected: 530 Permission denied

Anonymous login rejected. FTP requires credentials. The web application on port 80 is the next target.

Web Application: Security Dashboard

Gunicorn security dashboard showing network monitoring interface logged in as nathan

The dashboard is a security monitoring web app. The interface is already authenticated as nathan. A network capture feature generates PCAP files and serves them at a predictable URL:

http://10.129.26.15/data/1

A sequential integer ID. No ownership validation visible in the response. That is an IDOR waiting to be tested.


Foothold

IDOR: /data/0

The current session generates /data/1. Decrementing the ID to /data/0 retrieves a capture from an earlier session belonging to a different user. The server returns it with no error, no redirect, and no authorisation check.

Browser showing /data/0 returning a PCAP download from a different user's session with no access control
Finding

PCAP files are stored at sequential integer IDs with no ownership check. Any authenticated user can retrieve any other user's packet capture by decrementing the ID. This is OWASP A01:2021 (Broken Access Control). Session ID 0 contains traffic from a privileged user's earlier session.

PCAP Analysis: FTP Credentials in Plaintext

Open 0.pcap in Wireshark and apply an FTP display filter to isolate the control channel traffic:

Wireshark filter: ftp
Wireshark showing FTP control channel packets in the PCAP capture from session 0

Follow the TCP stream on any FTP packet to view the full authentication exchange:

Wireshark TCP stream showing FTP session with USER nathan and PASS Buck3tH4TF0RM3! in plaintext
220 (vsFTPd 3.0.3)
USER nathan
331 Please specify the password.
PASS Buck3tH4TF0RM3!
230 Login successful.
Finding

FTP transmits credentials as raw ASCII over port 21. The username nathan and password Buck3tH4TF0RM3! are visible in full in the TCP stream. Combined with the IDOR, anyone who can reach the /data/0 endpoint owns these credentials.

SSH with Recovered Credentials

FTP credentials recovered. SSH is open on port 22. Password reuse is common. Test it immediately:

$ ssh nathan@10.129.26.15
SSH login successful as nathan on Cap with password Buck3tH4TF0RM3!
Finding

FTP credentials accepted on SSH. The same password was reused across two different services. Credential isolation per service is fundamental. A single compromised protocol should not grant access to every other service on the host.

nathan@cap:~$ cat ~/user.txt
User flag captured from /home/nathan/user.txt
USER 2c49d2eedc3084ab2357ae6e1e232f8f

Privilege Escalation: cap_setuid on Python 3.8

nathan@cap:~$ whoami && id
whoami and id output showing nathan uid=1001 with no privileged group memberships

Standard user. No sudo rights, no interesting group memberships. Check Linux capabilities, a separate vector from SUID binaries that is missed by find -perm -4000:

nathan@cap:~$ getcap -r / 2>/dev/null
getcap output showing /usr/bin/python3.8 = cap_setuid,cap_net_bind_service+eip
/usr/bin/python3.8 = cap_setuid,cap_net_bind_service+eip
/usr/bin/ping      = cap_net_raw+ep
Finding

cap_setuid+eip is set on /usr/bin/python3.8. The cap_setuid capability allows the process to call setuid() and change its effective UID to any value, including 0. The +eip flags mean the capability is Effective, Inherited, and Permitted. It is active the moment Python is invoked. No SUID bit. Invisible to find -perm -4000.

With cap_setuid on a scripting runtime, root is a one-liner. Call os.setuid(0) to set the process UID to root, then spawn a shell that inherits it:

nathan@cap:~$ python3.8 -c "import os; os.setuid(0); os.system('/bin/bash')"
Python3.8 os.setuid(0) one-liner spawning a root bash shell
root@cap:~# cat /root/root.txt
Root flag captured from /root/root.txt
ROOT 552527d143bc50662bf065fafa6b2ef2
HackTheBox Cap solved confirmation screen, pwned 29 April 2026

Attack Chain

StepTechniqueResult
1Nmap two-phase scanFTP 21, SSH 22, HTTP 80 (Gunicorn) identified
2FTP anonymous login attemptRejected. Credentials required
3Web dashboard at port 80Security monitoring app, logged in as nathan, PCAP download at /data/<id>
4IDOR: GET /data/0Privileged session PCAP downloaded with no authorisation check
5Wireshark FTP filter + TCP streamnathan:Buck3tH4TF0RM3! extracted from plaintext FTP session
6SSH with FTP credentialsShell as nathan, user flag captured
7getcap -r / 2>/dev/nullcap_setuid+eip on /usr/bin/python3.8 discovered
8python3.8 os.setuid(0) one-linerRoot shell, root flag captured

Vulnerabilities Found

VulnerabilityLocationImpact
IDOR on /data/<id>Security dashboard PCAP endpointAny authenticated user can retrieve any other session's PCAP
FTP plaintext credentialsPCAP from session 0Username and password recovered from captured traffic
Credential reuse across servicesnathan accountFTP password valid for SSH, expanding foothold surface
cap_setuid on Python 3.8/usr/bin/python3.8Instant privilege escalation to root via one-liner

Lessons Learned

  • IDOR on sequential numeric IDs is worth testing on every endpoint. Any URL containing an integer is a candidate. Decrement to 0 immediately. The check costs nothing and a high percentage of web applications fail to validate object ownership server-side.
  • FTP transmits credentials in cleartext. USER and PASS commands travel as raw ASCII on port 21. If a PCAP exists containing FTP traffic, filter for it immediately. No special decoding required. The credentials are human-readable in the TCP stream.
  • Test every recovered credential against every open service before moving on. Credential reuse is extremely common. Buck3tH4TF0RM3! from FTP worked on SSH. Always spray horizontally across all open ports with any newly found credential.
  • getcap is a separate check from find -perm -4000. SUID bits and Linux capabilities are two different privilege escalation vectors. Running find -perm -4000 misses capabilities entirely. getcap -r / 2>/dev/null should be standard in every post-foothold checklist.
  • cap_setuid on a scripting runtime is instant root. Python, Ruby, Perl, and similar runtimes with cap_setuid all have the same one-liner exploit: call setuid(0) then spawn a shell. The capability is as dangerous as a SUID bit and significantly less visible.
  • The 2>/dev/null pattern is critical on noisy recursive commands. Without it, getcap floods output with permission denied errors from restricted directories. Redirect stderr to keep output clean and catch the real findings.
Next Samba 3.0.20. CVE-2007-2447. Root Without a Privesc Step.
Found this useful?

Share it with your network.