Busqueda Writeup

June 6, 2023 | 5 minutes

Busqueda was an Easy Linux machine featuring a vulnerable version of a web application, following with an enjoyable privilege escalation journey. Initial access led to credential exposure of our user. We used this to expose a script we could run as sudo, which consisted of light code review and eventually path hijacking to obtain root.

Reconnaissance

# Nmap 7.94 scan initiated Wed Jul  5 12:54:30 2023 as: nmap -p- -o nmap.txt 10.10.11.208
Nmap scan report for 10.10.11.208
Host is up (0.081s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

# Nmap done at Wed Jul  5 12:55:37 2023 -- 1 IP address (1 host up) scanned in 67.65 seconds

Initial Access

Before I could even do a verbose scan (nmap -sCV ...), I visited the website at searcher.htb since the IP auto-redirected to the domain which I put into my /etc/hosts.

Although I initially messed around with the features of the site which essentially allowed you to select a search engine and make searches through it, what caught my eye was that it was running on Flask and a specific web application called Searchor. They had also kindly provided the version number. With this information I was able to search for Searchor exploit github, where I landed on this POC Python exploit. Surely enough, the version applicability lined up and I ran the exploit in order to receive a reverse shell:

VHost Discovery

I landed in /var/www/app which is where the website was being hosted. Looking more closely, I found a .git directory. In the config file, I found some creds, as well as the virtual host gitea.searcher.htb.

I added gitea.searcher.htb to my /etc/hosts in order to access the site being hosted there. At the site, I’m greeted with gitea, a self-hosted Git server. I was able to successfully login as cody here:

I wasn’t able to find anything significant, and I was pretty confident that the files in the .git directory were synced with the site, therefore I decided to pivot and look around more.

Although the password I found in the git config file is for a user named cody, password reuse is common and is also present in boxes. Therefore, when I find a password, I like to try them with all the users I have. Since one of the things on my privilege escalation checklist is to check for forbidden and permitted commands for my user with sudo -l, I tried authenticating with cody’s password’:

Despite the password corresponding with user cody, I was able to successfully authenticate as user svc with the discovered password.

Docker Script

With the output of sudo -l, I discovered that I was able to run /usr/bin/python3 /opt/scripts/system-checkup.py *. Running the script as sudo showed the following:

I try messing with all the options, and I notice that full-checkup returns something went wrong no matter the input. I leave it for the moment while I look at the other options.

After doing some research, I discovered that docker-inspect was likely in reference to the actual docker inspect command, which returns a set of specified information on a container. The option docker-inspect expected a specified format as well as a container_name. While the names of the 2 existing containers could be found by running docker-ps, the list of formats required looking at docker’s documentation for the command. To confirm that these were the formats that the script was expecting, I tested a format that would return the IP address of the container:

Success :) So with the docker-inspect option, I could obtain information on the container… but what information could I even extract? I wasn’t too familiar with the formatting, so I searched up some examples. One of the prominent examples I found was a format that specified environment variables:

Boom, creds B) Not just one password, but two ![[Pasted image 20230705202226.png]]

I attempted logging into the gitea panel as administrator, as it was the only other user on the gitea panel besides cody:

Hey look, a scripts repository, the same name as the directory where system-checkup.py is located. Looking at the directory, I find the source code to the scripts.

Darn, a rookie error! When the option full-checkup is passed, system-checkup.py calls the script by relative path. Unfortunately, well, fortunately for me, when relative paths are used, the system searches it’s PATH variable first, and then the local directory. Since the script is running as root, it will call full-checkup.sh as root.

...

    elif action == 'full-checkup':
        try:
            arg_list = ['./full-checkup.sh']
            print(run_command(arg_list))
            print('[+] Done!')
        except:
            print('Something went wrong')
            exit(1)
...

Essentially, I can run anything I want as root by:

  1. creating my own full-checkup.sh script
  2. exporting a new PATH location that contains my full-checkup.sh payload
  3. running system-checkup.py and using the full-checkup option that will call my payload script rather than the local .full-checkup.sh script