Machine: Snapped
Difficulty: Hard
OS: Linux
Overview
Snapped is a hard-difficulty Linux machine that chains two recent CVEs to go from
unauthenticated access all the way to full root.
- CVE-2026–27944 — Nginx-UI unauthenticated backup endpoint that returns its own decryption key, leaking credentials from the internal database.
-
CVE-2026–3888 — A TOCTOU race condition between
snap-confineandsystemd-tmpfiles, exploited via an AF_UNIX socket trick to hijack the dynamic linker on a SUID-root binary.
Reconnaissance
Nmap
nmap [target-ip] -sV -sC -o scans/nmap
The scan reveals two open TCP ports: SSH (22) and HTTP (80). The HTTP server
redirects to http://snapped.htb, so we add that to our hosts file.
echo "[target-ip] snapped.htb" | sudo tee -a /etc/hosts
Initial Web Enumeration
The main site at snapped.htb is a static page with no obvious attack surface. Time to
look for virtual hosts.
Subdomain Discovery
We use ffuf to brute-force virtual hosts, filtering responses with the same size as
the default "not found" page (154 bytes):
ffuf -u http://snapped.htb -H "Host: FUZZ.snapped.htb" \
-w /usr/share/wordlists/dirb/common.txt -fs 154
We find a valid subdomain: admin. Add it to /etc/hosts and navigate to it.
The subdomain serves a Nginx-UI admin panel — a third-party web interface for
managing Nginx configurations.
Exploitation — CVE-2026–27944 (Nginx-UI Unauthenticated Backup Disclosure)
What is the vulnerability?
Nginx-UI exposes a /api/backup endpoint that creates and serves a full archive of all
Nginx and Nginx-UI configuration files — including its internal SQLite database, SSL
private keys, and session tokens. The critical flaw is two-fold:
- The endpoint requires no authentication — any external user can call it directly.
- The backup archive is AES-encrypted, but the decryption key is returned in the
X-Backup-Securityresponse header of the very same request.
This means an attacker can download the backup and immediately decrypt it in a single
unauthenticated HTTP request. There is no need to brute-force or guess anything at this
stage.
Setting up the exploit
nano cve.py
python3 -m venv venv
source venv/bin/activate
pip install pycryptodome
We need pycryptodome because the backup uses AES encryption, and the script needs
this library to decrypt the archive using the key extracted from the response header.
Running the exploit
python3 cve.py --target http://admin.snapped.htb --out backup.bin --decrypt
Under the hood, the script does the following:
- Sends a
GET /api/backuprequest with no credentials. - Reads the
X-Backup-Securityheader from the response to obtain the AES key. - Decrypts the backup archive using that key.
- Extracts the contents to
backup_extracted/.
Initial Access
Finding credentials in the backup
After exploring backup_extracted/, we find a SQLite database file — this is Nginx-UI's
internal user database. We can inspect it directly:
sqlite3 database.db
.tables
SELECT * FROM users;
We find a user jonathan with a bcrypt password hash:
$2a$10$8M7JZSRLKdtJpx9YRUNTmODN.pKoBsoGCBi5Z8/WVGO2od9oCSyWq
Cracking the hash
Save this to hash.txt and crack it offline with Hashcat. Bcrypt is slow by design
(mode -m 3200), but weak passwords are still crackable:
hashcat -m 3200 hash.txt /usr/share/wordlists/rockyou.txt



















Top comments (0)