← Back to Feed CACHED · 2026-07-22 09:27:05 · CACHE_KEY CVE-2026-8933
CVE-2026-8933 · CWE-250 · Disclosed 2026-07-21

snap-confine set-capabilities TOCTOU race enabling local root on Ubuntu

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

The bouncer stamped your hand before checking your ID, and you swapped the ink for a master key while he wasn't looking

CVE-2026-8933 is a local privilege escalation in snap-confine, the sandbox helper shipped with Canonical's snapd. Qualys chained two race conditions that appeared when Canonical hardened snap-confine from *setuid-root* to a *set-capabilities* model: because the binary now runs with the caller's UID (but with CAP_SYS_ADMIN-class capabilities retained), the scratch directories it creates in /tmp are momentarily owned by the attacker. A FUSE mount over that scratch dir plus a symlink/permission race lets the attacker plant a rogue file into /run/udev/rules.d/, and a subsequent FUSE cycle triggers systemd-udevd to execute arbitrary commands as root. Affected: default installs on Ubuntu 24.04, 25.10, 26.04, plus older LTS with snapd (16.04/18.04/20.04/22.04 via ESM/updates). Patched versions: snapd 2.76+ubuntu24.04.1, 2.76+ubuntu26.04.3, 2.76+ubuntu22.04.1, and ESM backports for older LTS.

Vendor HIGH / 7.8 is defensible on paper — it's AV:L/PR:L with full CIA impact — but for a fleet of 10,000 hosts the practical severity depends entirely on who has an interactive shell. Ubuntu Desktop with untrusted users? This is a real HIGH. Fleet of headless server VMs with no interactive users, no snapd installed, or snapd removed on the golden image? This is *noise*. Most enterprise Ubuntu Server fleets don't install snapd at all, and where it exists the attacker prerequisite (local shell as a non-root user) already implies prior compromise. Net: downgrade to MEDIUM for typical server fleets, keep at HIGH for shared-shell or student/dev workstation estates.

"Local root on Ubuntu Desktop via a snap-confine TOCTOU — real bug, but no remote surface and Desktop skews consumer, not fleet."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Attacker obtains an unprivileged local shell

The attack begins with any UID that can exec a snap. On a default Ubuntu Desktop this is trivially any logged-in user. On a server it means the attacker already has a foothold — via SSH creds, an app-tier RCE, or a container escape.
Conditions required:
  • Local shell / code execution as any non-root user
  • snapd package installed and snap-confine present
  • At least one installable snap available to invoke
Where this breaks in practice:
  • Server fleets increasingly ship without snapd (minimal/cloud images, Debian-based derivatives strip it)
  • Kiosk / appliance builds run as a single confined user with no snap invocation surface
Detection/coverage: auditd execve of /usr/lib/snapd/snap-confine from unusual UIDs; EDR process-tree anomalies
STEP 02

Race snap-confine's /tmp scratch directory with a FUSE mount

snap-confine now runs with the caller's effective UID and creates a temporary scratch directory in /tmp before applying the mount namespace. The attacker's exploit (Qualys PoC harness) mounts a FUSE filesystem over that directory in the window between creation and namespace lockdown, sidestepping the isolation snap-confine believed it had.
Conditions required:
  • Unprivileged FUSE mounts enabled (default on Ubuntu Desktop; often disabled on hardened server images)
  • Fast, deterministic timing — Qualys reports reliable single-digit-attempt success
Where this breaks in practice:
  • Disabling unprivileged FUSE (/etc/fuse.conf + kernel sysctl fs.protected_*) or removing the fuse package breaks step 2 entirely
  • Some container/VM guest configs already ban user_allow_other
Detection/coverage: auditd on mount syscalls with fstype fuse* from non-root; falco rule for FUSE mounts inside snap sandboxes
STEP 03

Symlink + permission race to widen a file to 0666 before fchown to root

During sandbox setup snap-confine calls open() on files inside the (attacker-controlled) scratch dir. The attacker plants a symlink so open() follows it to an attacker-chosen target. A second window between fchmod() and fchown() lets the file end up mode 0666 and owned by root.
Conditions required:
  • Successful FUSE mount from step 2
  • Kernel without fs.protected_symlinks=1 semantics blocking the specific path (most distros default protected_symlinks=1 but the race path bypasses via the FUSE fd)
Where this breaks in practice:
  • AppArmor snap-confine profile in enforce mode restricts the write targets
  • Custom seccomp on the snap runtime rejects the syscall pattern
Detection/coverage: auditd file-watch on /run/udev/rules.d/ — any write from a non-root/non-udev UID is high signal
STEP 04

Drop rogue rule into /run/udev/rules.d and trigger udevd

Attacker uses the root-writable path from step 3 to place a malicious .rules file in /run/udev/rules.d/. A follow-on FUSE mount/unmount cycle generates a uevent, and systemd-udevd reads the rule and executes the embedded RUN+= command as root. Weaponized tool: Qualys snap-confine-lpe PoC (private at time of writing, expected on GitHub within days).
Conditions required:
  • systemd-udevd running (default)
  • Ability to trigger a uevent (any (u)mount, device add — trivial from userland)
Where this breaks in practice:
  • Immutable /run overlays on some appliance builds
  • SELinux (on Ubuntu with SELinux, rare) confining udevd
Detection/coverage: auditd rule on RUN+= execution by systemd-udevd with parents outside package-managed rules — trivial detection engineering win
STEP 05

Root shell / persistence

Arbitrary command as UID 0. From here: add SSH key to /root/.ssh/authorized_keys, install a kernel module, disable EDR, pivot laterally with cached creds. This is the classic post-LPE playbook.
Conditions required:
  • Steps 1-4 succeeded
Where this breaks in practice:
  • Runtime EDR (CrowdStrike, SentinelOne, Elastic Defend) with kernel-level exec monitoring catches the udevd-spawned reverse shell in most tuned environments
Detection/coverage: Any competent EDR should alert on udevd → bash/curl/wget/nc
03 · Compensating Control

1
Refresh snapd on all Ubuntu hosts within 30 days — Run sudo snap refresh snapd core and sudo apt install --only-upgrade snapd. Snap auto-refresh will handle most hosts within 24h — verify the minority that disabled it. Deploy within the noisgate mitigation SLA of 30 days for HIGH-tier assets (multi-user, CI runners); MEDIUM assets fall under the 365-day remediation window.
2
Inventory which hosts actually have snapd installed — Run dpkg -l snapd or rpm -q snapd fleet-wide. Any host without snapd is not affected — this dramatically narrows the patching universe on server fleets. Do this in the first 48 hours to size the actual exposure.
3
Remove snapd from headless server golden images — If snapd is not required (most server workloads don't need it), apt purge snapd && apt-mark hold snapd on the base image. Eliminates this class of vuln permanently. Roll into next image-baking cycle (~30 days).
4
Disable unprivileged FUSE mounts on hardened hosts — Remove user_allow_other from /etc/fuse.conf, set kernel sysctl kernel.unprivileged_userns_clone=0 where feasible. Breaks step 2 of the chain and blocks a broader class of container-escape / LPE primitives. 7-day window on jump boxes and shared-shell hosts.
5
Add auditd rule watching /run/udev/rules.d/ writes and udevd exec children — -w /run/udev/rules.d/ -p wa -k udev_rule_tamper plus a rule on execve where the parent comm is systemd-udevd. Any write by non-root or udevd spawning a shell is a fire-alarm event. Deploy in 3-7 days as a durable detection.
6
Force-enable AppArmor enforce mode for snap-confine — Verify aa-status | grep snap-confine shows enforce, not complain. Some hardening tools drop AppArmor to complain — that widens the attack surface for step 3. Verify in 7 days.
What doesn't work
  • Perimeter firewalls / WAF — this is local-only. Network controls provide zero value against AV:L.
  • Removing sudo access from the attacker's user — the exploit doesn't touch sudo; it uses snap-confine's own capabilities, not the caller's.
  • Read-only /tmp mounts — snap-confine creates its own scratch namespace; a read-only bind mount over the parent /tmp doesn't cover the sub-mounts snap-confine sets up.
  • Disabling snap auto-refresh — this *prevents* the fix from being applied. Do the opposite: verify auto-refresh is enabled.
  • MFA / identity controls — this is post-authentication. MFA on the initial login is fine, but doesn't stop an attacker who's already a logged-in user.
04 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationNone observed as of 2026-07-22. Disclosure was ~24 hours ago; expect PoC drops and opportunistic use within days.
KEV statusNot listed on CISA KEV as of 2026-07-22.
Public PoCQualys published a full technical writeup with exploit primitives but has not released the harness. Expect a working PoC in snap-confine-lpe-style repos within a week — the sibling bug CVE-2026-3888 had a working PoC within 72 hours of disclosure.
EPSSNot yet scored (CVE < 48h old). Expect <5% initial, rising sharply once PoC lands.
CVSS vectorCVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H — local, low complexity, low privileges, no user interaction. Vector is honest.
Affected versionssnapd before 2.76+ubuntu24.04.1 (Noble), 2.76+ubuntu26.04.3 (Resolute), 2.76+ubuntu22.04.1 (Jammy); ESM backports for Focal 2.67.1+20.04ubuntu1~esm3, Bionic 2.61.4ubuntu0.18.04.1+esm4, Xenial 2.61.4ubuntu0.16.04.1+esm4.
Fixed versionsSnap store auto-refresh delivers snapd 2.76 within 24h on hosts with default auto-refresh; APT users need apt install --only-upgrade snapd.
ReporterQualys Threat Research Unit (TRU) — same team behind Looney Tunables, PwnKit, and CVE-2026-3888. Track record of reliable, weaponizable LPEs.
DisclosureCoordinated disclosure 2026-07-21 via Canonical / Ubuntu Security Team.
Exposure telemetryNot applicable — local-only vuln. No Shodan/GreyNoise signal possible. Population estimate: ~40M Ubuntu Desktop installs with snapd; highly variable on Ubuntu Server (minimal cloud images increasingly ship without snapd).

Sources.

  1. Qualys TRU — CVE-2026-8933 technical writeup
  2. Ubuntu Security — snapd multiple vulnerabilities fixed
  3. Debian Security Tracker — CVE-2026-8933
  4. Qualys TRU — CVE-2026-3888 (sibling snap-confine LPE, March 2026)
  5. oss-security — snap-confine + systemd-tmpfiles = root (CVE-2026-3888 background)
  6. Elastic Security — detection rule for snap-confine LPE
  7. The Hacker News — Ubuntu snap-confine root escalation coverage
  8. ITBrief — Ubuntu snap-confine flaw grants root
05 · The Call

Final Verdict
↓ DOWNGRADED to MEDIUM (6.4/10)

Why this verdict

  • Local-only attack vector (AV:L) — the vector string honestly reflects that the attacker must already be executing code on the box. This removes the internet-exposure risk that drives most HIGH/CRITICAL calls.
  • Prerequisite implies prior compromise on server fleets — on a headless production Ubuntu Server, an unprivileged local shell means the intrusion clock already started. This chain is the *second* stage, not the first.
  • Installed-base narrowing — Ubuntu minimal cloud images (ubuntu-minimal, most container base images, AWS/GCP/Azure minimal images) ship without snapd. On those hosts the vuln is *unreachable*. Qualys' own writeup pins impact to Ubuntu Desktop defaults.
  • Role multiplier — workstation / dev sandbox: chain succeeds, blast radius = single host. No fleet amplification. This is the modal case.
  • Role multiplier — line-of-business Ubuntu Server: chain succeeds *if* snapd is present and an unprivileged user has a shell; blast radius = single host with credentialed pivot potential (SSH keys, cloud IAM tokens, k8s kubeconfig). Meaningful but bounded.
  • Role multiplier — high-value roles (domain controller, IdP, hypervisor, PAM, CI runner): none of these are canonically Ubuntu-with-snapd. CI runners are the one plausible high-value analogue (GitHub Actions self-hosted, GitLab Runner on Ubuntu) — if a build job can drop this exploit, root on the runner = supply-chain pivot. That scenario keeps the floor at HIGH *for CI runner fleets specifically*, but does not lift the general verdict, because CI runners are <10% of the affected component's install base.
  • EDR safety net — udevd spawning bash/curl/nc is a signature detection layup for any competent EDR.

Why not higher?

HIGH would be appropriate if this were remotely reachable or if snapd were canonically deployed on identity/hypervisor/CI infrastructure. Neither holds: AV:L and the affected component is a desktop-oriented sandbox helper. Multi-user shared-shell environments (universities, HPC clusters, jump boxes) *should* treat this as HIGH — bump the verdict locally in those estates.

Why not lower?

LOW / IGNORE would ignore that this is a reliable, low-complexity path from unprivileged shell to root on default Ubuntu Desktop, published by a team (Qualys TRU) that reliably ships weaponizable exploits within days. Any environment with shared interactive access, student accounts, or CI runners must patch on the HIGH timeline.

06 · Verification

Crowdsourced verification payload.

Run on each target Ubuntu host as a normal user (no sudo required for the read-only checks). Example: ./check-cve-2026-8933.sh. Exits 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN / snapd not installed.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check-cve-2026-8933.sh — noisgate verification for snap-confine LPE
# Determines whether the running snapd package is at or above the fixed version
# for this host's Ubuntu release.
set -u

if ! command -v dpkg >/dev/null 2>&1; then
  echo 'UNKNOWN: dpkg not available (non-Debian host?)'
  exit 2
fi

if ! dpkg -l snapd 2>/dev/null | grep -q '^ii'; then
  echo 'PATCHED: snapd not installed — CVE-2026-8933 not applicable'
  exit 0
fi

INSTALLED=$(dpkg-query -W -f='${Version}' snapd 2>/dev/null)
RELEASE=$(. /etc/os-release; echo "$VERSION_ID")

case "$RELEASE" in
  16.04) FIXED='2.61.4ubuntu0.16.04.1+esm4' ;;
  18.04) FIXED='2.61.4ubuntu0.18.04.1+esm4' ;;
  20.04) FIXED='2.67.1+20.04ubuntu1~esm3'   ;;
  22.04) FIXED='2.76+ubuntu22.04.1'         ;;
  24.04) FIXED='2.76+ubuntu24.04.1'         ;;
  25.10) FIXED='2.76+ubuntu25.10.1'         ;;
  26.04) FIXED='2.76+ubuntu26.04.3'         ;;
  *)     echo "UNKNOWN: unrecognized Ubuntu release $RELEASE (installed=$INSTALLED)"; exit 2 ;;
esac

# dpkg --compare-versions returns 0 if the relation holds
if dpkg --compare-versions "$INSTALLED" ge "$FIXED"; then
  echo "PATCHED: snapd $INSTALLED >= $FIXED (Ubuntu $RELEASE)"
  exit 0
else
  echo "VULNERABLE: snapd $INSTALLED < $FIXED (Ubuntu $RELEASE) — CVE-2026-8933"
  exit 1
fi