root@thehacksparrow:~/writeups$ SYSTEM ONLINE
root@sparrow:~/writeups$ cat cap.md
// writeups

Cap

5 Jun 2021 · 10 min read
Cap - maquina de Hack The Box

Executive summary — Cap is a very elegant Easy Linux box with no public exploit involved: everything rests on design flaws. An IDOR in a «security» dashboard lets us download another session's network capture (.pcap), where cleartext FTP credentials travel; with them we log into FTP and SSH (password reuse), and the final escalation abuses a Linux capability (cap_setuid) granted to python3.8 to become root without touching sudo or SUID. An IDOR → PCAP → FTP/SSH → capabilities → root path.

PlatformHack The Box
Operating systemLinux
DifficultyEasy
StatusRetired
Target IP10.129.17.19

Attack map

[80] security dashboard · /capture → /data/1
   │  IDOR: change the ID → /data/0 downloads 0.pcap (another session capture)
   ▼
[PCAP]  Wireshark → cleartext FTP traffic → nathan:Buck3tH4TF0RM3!
   ▼
[FTP]  login nathan → user.txt
   ▼
[SSH]  nathan → interactive shell
   │  find -perm -4000 → nothing useful · getcap → /usr/bin/python3.8 = cap_setuid+ep
   ▼
[ROOT]  python3.8 -c "os.setuid(0); os.system('/bin/bash')"

1. Reconnaissance

As always, we start with a full port scan instead of trusting nmap's default top-1000: on HTB boxes you never know which «odd» service is hiding on a high port.

nmap -p- --open -n -T5 -v 10.129.17.19

Why these flags:

  • -p- → scans all 65535 ports, not just the most common ones.
  • --open → only shows ports that answer as open, keeping the output clean.
  • -n → no DNS resolution, faster scan.
  • -T5 → very aggressive timing template (lab network, no IDS to worry about).
  • -v → verbose mode, so ports show up as they're confirmed.
21/tcp  open  ftp
22/tcp  open  ssh
80/tcp  open  http

Analysis: three very classic services —FTP, SSH and HTTP—. The «web with something interesting + FTP with credentials + SSH to get in» pattern is common on Easy boxes, so we start with the web.

2. Web enumeration: the dashboard

We browse to http://10.129.17.19 and find a security dashboard: an application supposedly built to capture and analyse network traffic. Poking at its routes by hand turns up one that stands out:

/capture

Visiting http://10.129.17.19/capture makes the server generate a new network capture (a .pcap) and redirects us to:

http://10.129.17.19/capture   →   /data/1

What's happening: the app numbers each capture and serves it at /data/<id>. Indexing resources by a sequential, predictable number, without checking that the capture actually belongs to whoever requests it, is the exact recipe for an IDOR (Insecure Direct Object Reference).

3. Key vulnerability — IDOR

If the server never validates that capture 1 is «ours», nothing stops us from requesting a different number. We try the most obvious one, 0 —usually the oldest ID, often generated by an automated process or by the admin—:

http://10.129.17.19/data/0

Result: we download 0.pcap, a network capture that isn't ours. The flaw isn't technical or sophisticated: it's a total absence of object-level authorisation. Authentication (if there even is any) doesn't matter if anyone can request anyone else's resource by changing a number in the URL.

4. PCAP analysis → FTP credentials

We open the downloaded capture in Wireshark and filter for the protocol we care about most when credentials are involved:

wireshark 0.pcap    # display filter: ftp

Among the filtered packets, a full FTP login shows up in plain text:

USER nathan
PASS Buck3tH4TF0RM3!

Why it works: plain FTP (not FTPS) encrypts neither the control channel nor the data: username and password travel in the clear inside the TCP packets. Anyone who captures that traffic —or downloads the capture, as here— gets the credentials without breaking anything.

5. Initial access — FTP

We try the recovered credentials directly against the service they came from:

ftp 10.129.17.19
# Username: nathan
# Password: Buck3tH4TF0RM3!

Once logged in, we switch to binary mode (to avoid corrupting the file) and grab the user flag:

binary
get user.txt
cat user.txt

6. Interactive access — SSH

FTP gets us the flag, but it's a session limited to file transfer: no good for enumerating the system or escalating privileges. We try the same username/password pair against SSH, betting that nathan reuses the same password across services:

ssh nathan@10.129.17.19
# password: Buck3tH4TF0RM3!

Credential reuse: it works, giving us a full interactive shell as nathan —the real starting point for the escalation phase.

7. Enumeration for privilege escalation

Before looking at anything exotic, we check the classic first: binaries with the SUID bit set, which run with their owner's privileges instead of the invoking user's:

find / -perm -4000 -type f 2>/dev/null

Nothing out of the ordinary: the SUID binaries are the usual ones shipped with any distribution (passwd, sudo, etc.), no exploitable candidate. We move on to the modern equivalent of SUID: Linux capabilities.

getcap -r / 2>/dev/null
/usr/bin/python3.8 = cap_setuid+ep

There it is: unlike SUID (which affects the whole binary), here an entire general-purpose interpreter, python3.8, has been granted a very powerful capability.

8. Vulnerability — Linux Capabilities

Capabilities are the Linux kernel's way of slicing up root's privileges into smaller pieces and handing a specific one to a specific binary, instead of giving it all of root's power (as the classic SUID bit does) or none at all. In theory this is a more secure model than SUID… if configured carefully.

The problem with cap_setuid on an interpreter: this capability lets the process change its own UID, including to another user's or to root's (UID 0). Granting it to a closed, single-purpose binary is reasonably safe; granting it to a general-purpose interpreter like Python hands over the keys to the house —from Python you can run any code, including code that becomes root.

9. Escalating to root

We don't need to start as root or go through sudo: we simply ask the interpreter (which already holds the privilege) to change its own UID to 0 and spawn a shell:

/usr/bin/python3.8 -c 'import os; os.setuid(0); os.system("/bin/bash")'

What each line does:

  • os.setuid(0) → thanks to cap_setuid, the Python process changes its effective UID to 0 (root).
  • os.system("/bin/bash") → spawns a shell that inherits that already-elevated UID.
id  →  uid=0(root)

10. Root flag

cat /root/root.txt

System fully compromised. 🏴‍☠️

Vulnerability summary

PhaseComponentVulnerabilityImpact
Info leakWeb app (/data/<id>)IDOR: no object-level access control on captures indexed by IDDownload of other sessions' network captures
Info leakNetwork capture (PCAP)Cleartext FTP traffic inside the leaked captureRecovery of valid credentials (nathan)
FootholdFTP / SSHSame password reused across services, with no extra controlFTP session (user.txt) and interactive SSH shell as nathan
PrivEscpython3.8cap_setuid capability granted to a general-purpose interpreterExecution as root

Lessons learned

  • IDOR: referencing objects by a sequential, predictable ID without checking resource ownership exposes other users' data. Authorise every access, don't just authenticate.
  • FTP is cleartext: never transmit or store credentials over FTP; use FTPS or SFTP.
  • Network captures are gold for an attacker: an accessible .pcap can hold cleartext secrets captured from legitimate sessions.
  • Don't reuse passwords across services: a credential leaked on one protocol compromises every service that shares it.
  • Audit Linux capabilities, not just SUID: cap_setuid (or cap_setgid, cap_dac_override…) on a general-purpose interpreter is equivalent to handing out root access. Review with getcap -r / as thoroughly as you'd review SUID binaries.