To solve this machine, we begin by enumerating open ports using
nmap – finding ports
80 open. From the web server, we find a Server-Side Request Forgery (SSRF) vulnerability. Using the SSRF vulnerability, we are able to enumerate the web server, and eventually find credentials for an internal FTP server. Using the SSRF vulnerability to connect to the FTP server, we are able to get
user.txt and the user’s SSH private key. After using the private key to connect to the machine via SSH, we find we are able to run a python script as
root. Exploiting the script, we are able to get a shell on the machine as
root, and can read
Like all machines, we begin by enumerating open ports using
nmap. From our scans, we find ports
80 filtered or open.
$ sudo nmap -v -p- --min-rate 3000 $RHOST [...] $ sudo nmap -sV -A -oA enum/nmap/tcp-scripts $RHOST # Nmap 7.91 scan initiated Sat Sep 11 14:22:34 2021 as: nmap -sV -A -oA enum/nmap/tcp-scripts 10.129.213.186 Nmap scan report for forge.htb (10.129.213.186) Host is up (0.071s latency). Not shown: 997 closed ports PORT STATE SERVICE VERSION 21/tcp filtered ftp 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 3072 4f:78:65:66:29:e4:87:6b:3c:cc:b4:3a:d2:57:20:ac (RSA) | 256 79:df:3a:f1:fe:87:4a:57:b0:fd:4e:d0:54:c6:28:d9 (ECDSA) |_ 256 b0:58:11:40:6d:8c:bd:c5:72:aa:83:08:c5:51:fb:33 (ED25519) 80/tcp open http Apache httpd 2.4.41 ((Ubuntu)) |_http-server-header: Apache/2.4.41 (Ubuntu) |_http-title: Gallery No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ). TCP/IP fingerprint: OS:SCAN(V=7.91%E=4%D=9/11%OT=22%CT=1%CU=39144%PV=Y%DS=2%DC=T%G=Y%TM=613D021 OS:8%P=x86_64-pc-linux-gnu)SEQ(SP=FB%GCD=1%ISR=110%TI=Z%CI=Z%II=I%TS=A)OPS( OS:O1=M54DST11NW7%O2=M54DST11NW7%O3=M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST11 OS:NW7%O6=M54DST11)WIN(W1=FE88%W2=FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN( OS:R=Y%DF=Y%T=40%W=FAF0%O=M54DNNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS OS:%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R= OS:Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F= OS:R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T OS:=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD= OS:S) Network Distance: 2 hops Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel TRACEROUTE (using port 53/tcp) HOP RTT ADDRESS 1 155.09 ms 10.10.14.1 2 155.26 ms forge.htb (10.129.213.186) OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Sat Sep 11 14:23:04 2021 -- 1 IP address (1 host up) scanned in 29.94 seconds
From the results, we see that port
21 is filtered, which suggests it may be listening, but not responding to our connection attempt. Using tools like
nikto, we attempt to enumerate the web server, however, we do not find anything that looks particularly interesting. When we attempt to connect to the web server, we are redirected to
http://forge.htb. Since this FQDN does not exist, we add it to our
/etc/hosts file, and retry accessing the web server. This time, we are able to see what appears to be an image gallery.
From the “Upload an image” page, we are given the option to upload images either from a file, or from a URL.
Next, we try to test the URL uploader for validation checks, and see that the hostname and protocol are both checked. Additionally, we see that the FQDN
forge.htb is also blacklisted.
Next, we check if we are able to bypass the protocol and hostname restriction by converting the “localhost” ip to decimal, and try to access port
21 to confirm our suspicion of its use. We are successful, and see that port
21 is listening locally.
Since, we are able to upload from URLs, we see if we are able to get the machine to connect to our machine. Doing so, we see that we do get a connection from the machine, and that it’s using
python requests for the connection.
Next, we test the file uploader to see if we are able to upload server code and execute it. As a POC, we try to see if we can get
phpinfo() to execute (assuming php is installed with Apache), however, we see that the file is displayed back as an image.
Lastly, we attempt to enumerate possible vhosts using
ffuf, and find that
admin is a valid vhost. When we add the subdomain to our
/etc/hosts file and attempt to access it, we see that it is only accessible locally.
Since we know there is an internal admin subdomain, we need to bypass the hostname restriction in order to view it. Since we are able to get the URL image uploader to connect to our machine, we can try to send an HTTP redirect response to the admin site. If we are successful, we may be able to view the contents of the site – which means the image uploader is vulnerable to Server-Side Request Forgery (SSRF). To do this, we start by creating a file that will hold the redirect response.
$ cat redirect HTTP/1.1 301 Moved Permanently Date: Sat, 11 Sep 2021 19:20:22 GMT Server: Apache/2.4.41 (Ubuntu) Location: http://admin.forge.htb/
Next, we open a
netcat listener, and supply the redirect response as its input. When the uploader connects to our machine, it will receive the redirect response from
netcat. Lastly, we submit the upload request to connect to our machine on port
80. After the machine connects, we kill the
netcat connection to stop the connection from hanging. Once the connection closes, we get a response from the uploader that suggests the redirection was successful.
Once we open the link we are given to view the upload content, we look at it in BurpSuite (or whatever Proxy tool you use). From its content, we see the redirection was successful, and see it’s an admin page that includes a link to
Repeating the same process to see the announcements link, we get credentials to an internal FTP server. This confirms our suspicions as to why the
nmap scan for port
21 showed as “filtered”.
Once again, we attempt to use the redirection to connect to the FTP server. Doing so, we see that it’s the user’s
/home directory, and in it we see
user.txt – which we are able to read. Since our FTP access connects us to the user’s home directory, we can attempt to get the user’s ssh private key. Doing so, we do in fact get the user’s ssh key. We save it to our local machine, set the proper permissions, then connect to the SSH server as the user. If we did not use the SSRF vulnerability to read
user.txt, we can formally read it now.
Since we have a shell as
user, we start looking for ways that we may escalate our privileges to
root. The first thing we try to run is
sudo -l. Doing so, we see that we are able to run a
python script as
Looking at the script, we see that it spawns a local server on a random port. Once we connect to the server, we are able to run a few commands as
root after supplying the admin password. Interestingly enough, we see that the
pdb module is used in cases where an exception is thrown.
pdb.post_mortem, we find an article that explains
pdb, and what can be done with
pdb.post_mortem. From it, we see that the
! command allows us the execute
python code similarly to
exec() function. Since we can run the script as
root, we can abuse this functionality to get a shell on the machine as
root. To do so, we run the script as
root, connect to the server, and supply the admin password. Since we need to generate an exception to exploit the script, we can supply a string as the command to run since the program is expecting a number. Once we supply a string and trigger an exception, we give the command
! import os;os.system("/bin/bash") to get a shell as
root. With that, we can now read
Thank you for taking the time to read my write-up. I am interested in other ways this machine has been solved. Feel free to reach out to me and we can discuss it. Thanks!