Like a parking garage counter that rolls over to zero and lets the next car smash through the gate into the basement
CVE-2026-74469, nicknamed DiagSpill, is an integer-overflow-to-out-of-bounds-write in the Linux kernel's SCTP subsystem. The function sctp_assoc_add_peer() tracks peer transports in a 16-bit transport_count field. When the 65,536th unique peer is added, the counter wraps to zero. A subsequent sctp_diag diagnostic dump (triggered by ss -S or any Netlink SOCK_DIAG request) trusts that wrapped value to size its response buffer, then copies the full transport list — roughly 8 MiB of peer addresses — past the end of the allocated skb, corrupting adjacent kernel memory. The bug was introduced in kernel 4.7 (commit 8f840e47f190) and affects every mainline kernel through the fix commit bd0e9289e264. Fixed stable releases include 5.10.265, 5.15.216, 6.1.183, 6.6.151, 6.12.103, 6.18.44, 7.1.8, and 7.2-rc6. All RHEL versions 6 through 10 are affected.
The vendor-assigned CVSS 8.8 with AV:N overstates the realistic attack surface. The network vector assumes the remote ASCONF/ADD-IP path, which requires net.sctp.addip_enable=1 and either SCTP-AUTH or net.sctp.addip_noauth_enable=1 — both off by default on every major distribution. The practical exploit path is local: an unprivileged user creates an SCTP socket (auto-loading the module on permissive distros), accumulates 65,536 transports, and triggers a diag dump. The resulting 8 MiB heap overwrite is weaponizable for local privilege escalation to root, as demonstrated by two public PoC repositories. A realistic CVSS vector is AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H → 7.8. The severity label remains HIGH, but the score drops by a full point because the network preconditions almost never exist in production.
5 steps from start to impact.
Attacker obtains local shell
- Local unprivileged shell on a Linux host running a vulnerable kernel (4.7 – pre-fix)
- Requires prior access — this is a post-initial-access technique, not an entry vector
SCTP module auto-loads
AF_INET, SOCK_STREAM, IPPROTO_SCTP socket. On distributions that allow unprivileged module auto-loading (many Ubuntu, Debian, Fedora, and older RHEL configs), the kernel loads sctp.ko on demand. On RHEL 8+ and hardened hosts with install sctp /bin/false in /etc/modprobe.d/, this step fails and the chain is dead.- SCTP kernel module not blacklisted or already loaded
- Kernel config includes
CONFIG_IP_SCTP=m(default on most distro kernels)
- RHEL 8+ blacklists SCTP by default via
sctp-blacklist.conf - Hardened CIS/STIG builds explicitly block SCTP auto-load
- Container runtimes may restrict socket family creation via seccomp
-w /etc/modprobe.d/ -p wa), or auditctl -a always,exit -F arch=b64 -S init_module -S finit_module.Create 65,536 peer transports
0xBlackash/CVE-2026-74469 poc.c) opens an SCTP association and iterates through addresses to add 65,536 unique peer transports to a single association. This wraps the 16-bit transport_count to zero. The operation is I/O-intensive and takes measurable time (seconds to low minutes), generating significant network-stack activity.- Sufficient memory and address space to hold 65,536 transport entries (~8 MiB of kernel structures)
- No ulimit or cgroup memory cap that kills the process before completion
- Creating 65K transports is noisy and detectable by kernel memory-pressure alerts
- Some container memory cgroups will OOM-kill the process before the wrap
ss -S or custom eBPF probes on sctp_assoc_add_peer. Spike in kernel slab allocations for sctp_transport objects.Trigger sctp_diag dump
SOCK_DIAG request for SCTP sockets (equivalent to running ss -S). The sctp_diag module auto-loads if not already present. The diagnostic handler reads the wrapped transport_count (now 0), allocates a near-zero-length payload in the response skb, then copies all 65,536 transport addresses — approximately 8 MiB — past the buffer's tail pointer, causing a massive out-of-bounds heap write.- sctp_diag module loadable or already loaded
- Netlink SOCK_DIAG accessible to unprivileged users (default on most kernels)
- If
sctp_diagis also blacklisted, the dump cannot execute
sctp_diag_dump is the most common detection signal. KASAN-enabled kernels will report the OOB write immediately.Kernel memory corruption → LPE
- Heap layout must be groomed for reliable exploitation (vs. simple kernel panic)
- KASLR/SMAP/SMEP increase difficulty but do not prevent exploitation
- Unreliable on hardened kernels with full KASAN, CFI, or page-table isolation
- Kernel panic is the more common outcome than clean LPE on first attempt
- SELinux/AppArmor in enforcing mode may block post-exploitation pivots
dmesg BUG/OOPS messages, EDR agents monitoring for unexpected root transitions from unprivileged processes.install sctp /bin/false and install sctp_diag /bin/false to /etc/modprobe.d/nosctp.conf, then run update-initramfs -u (Debian/Ubuntu) or dracut -f (RHEL/Fedora). This kills the attack chain at step 2 — without the SCTP module, no association can be created and no transport counter can overflow. Deploy within 30 days per the noisgate mitigation SLA for HIGH. If SCTP is in active use (telecom, SIGTRAN, Diameter), this control is not applicable — proceed directly to patching.socket(AF_INET, *, IPPROTO_SCTP). Docker's default seccomp profile does NOT block this — you must add an explicit rule. Kubernetes Pod Security Standards at restricted level do not cover SCTP socket creation either. This prevents container-to-host escalation via DiagSpill. Deploy within 30 days.CONFIG_KASAN=y or use slab_nomerge and init_on_alloc=1 boot parameters. These raise the bar for reliable heap exploitation from the 8 MiB overwrite — the attacker gets a kernel panic instead of a clean root shell. This converts RCE/LPE risk to DoS risk. Deploy within 30 days.- Disabling unprivileged user namespaces — Red Hat explicitly notes this does NOT mitigate DiagSpill, unlike the other three CVEs in the quartet. DiagSpill requires no namespace manipulation.
- Network segmentation / firewall rules — The primary attack path is local, not network-based. Blocking SCTP at the firewall only addresses the already-unlikely remote ASCONF path.
- SELinux / AppArmor in enforcing mode — These MAC systems restrict post-exploitation actions but do not prevent the kernel memory corruption itself. The OOB write happens in kernel context before any LSM hook can intervene.
- ASLR / KASLR alone — Address randomization increases exploitation difficulty but does not prevent the 8 MiB overwrite. The corruption primitive is large enough to be weaponized probabilistically even with full KASLR.
The supporting signals.
| In-the-wild exploitation | No confirmed exploitation in the wild. Not listed in CISA KEV. No GreyNoise or Shadowserver tags observed for this CVE as of 2026-10-09. The vuln is part of a disclosure quartet (DirtyAH6, PPPoEject, TUNderflow, DiagSpill) that drew CISA attention for the other three — DiagSpill was notably absent from KEV. |
|---|---|
| Proof-of-concept availability | Two public PoC repositories: 0xBlackash/CVE-2026-74469 (C, poc.c) and HORKimhab/CVE-2026-74469 (DiagSpill-specific). Both indexed on Sploitus. PoCs demonstrate kernel crash and local-root under specific heap conditions. |
| EPSS | 0.01083 (≈1.08%) — low exploitation probability. Reflects the local-access requirement and SCTP module dependency that narrow the attacker population. |
| KEV status | Not listed as of 2026-10-09. Three sibling CVEs from the same disclosure (CVE-2026-80844, CVE-2026-81000, CVE-2026-68121) were added to KEV; DiagSpill was not, likely due to the SCTP module prerequisite. |
| CVSS vector | Vendor: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H (8.8). noisgate-adjusted: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H (7.8). The AV:N assumes remote ASCONF path, which requires non-default options (addip_enable, addip_noauth_enable). Realistic vector is AV:L. |
| Affected versions | Linux kernel 4.7 through pre-fix mainline. Introduced by commit 8f840e47f190. All RHEL 6–10 affected. Debian Bookworm < 6.1.187, Trixie < 6.12.111. |
| Fixed versions | Stable: 5.10.265, 5.15.216, 6.1.183, 6.6.151, 6.12.103, 6.18.44, 7.1.8, 7.2-rc6. Upstream fix: commit bd0e9289e264. Debian: DSA-6466-1, DLA-4777-1, DLA-4788-1. RHEL: RHSA-2026:71687, RHSA-2026:71565. |
| Scanning / exposure | Not remotely scannable in default configurations. SCTP exposure is minimal on internet-facing hosts — Shodan shows <50,000 hosts with SCTP services globally, a tiny fraction of the Linux installed base. The local-access requirement makes network scanning irrelevant for the primary attack path. |
| Disclosure timeline | Disclosed 2026-08-15 via linux-cve-announce. Public PoCs appeared 2026-09-18 alongside the DirtyAH6/PPPoEject/TUNderflow quartet disclosure on oss-security. ~34 days from disclosure to weaponized PoC. |
| Researcher / attribution | Disclosed through the Linux kernel CVE process. PoC authors: 0xBlackash and HORKimhab (GitHub). Part of the "Linux local-root quartet" research published on oss-security. |
Sources.
- Red Hat Security Bulletin RHSB-2026-011
- Debian Security Tracker — CVE-2026-74469
- 0xBlackash PoC — GitHub
- HORKimhab DiagSpill PoC — GitHub
- Linux local-root quartet disclosure analysis
- CISA flags three Linux kernel CVEs (sibling disclosure)
- Red Hat CVE page — CVE-2026-74469
- CONFIG_INET_SCTP_DIAG kernel config reference
Why this verdict
- AV:N → AV:L adjustment (−1.0 score): The vendor CVSS assumes AV:N via the remote ASCONF/ADD-IP SCTP path. This path requires
net.sctp.addip_enable=1AND either SCTP-AUTH ornet.sctp.addip_noauth_enable=1— both disabled by default on every major distribution. The realistic attack vector is local. - SCTP module availability narrows population: RHEL 8+ blacklists SCTP by default. CIS/STIG benchmarks for all distros recommend blacklisting. On permissive distros (stock Ubuntu, Debian, Fedora), unprivileged auto-load is possible, but hardened enterprise builds typically block it. Estimated reachable population: 40–60% of enterprise Linux hosts.
- Local access prerequisite implies post-initial-access: The attacker must already have an unprivileged shell. This is an escalation technique, not an entry vector. Every compensating control for initial access (MFA, SSH hardening, endpoint protection) reduces the population that reaches this step.
- Public PoC with LPE capability elevates above MEDIUM: Two GitHub repositories demonstrate kernel crash and environment-dependent root escalation. The 8 MiB overwrite is a powerful primitive. Even without 100% reliable LPE, the DoS (kernel panic) impact alone is significant on production hosts.
- Role multiplier: The Linux kernel spans all deployment roles. (a) *Workstations/dev boxes*: LPE yields root on one host — bounded blast radius. (b) *Application servers*: root access enables lateral movement, data exfiltration — host-scale impact. (c) *KVM hypervisors / Kubernetes nodes*: if SCTP auto-loads (possible on stock Ubuntu/Debian node images), LPE on the host = VM escape / container escape = fleet-scale impact. (d) *Samba AD DCs on Linux*: root = domain compromise. The affected component IS the kernel — canonically high-value — but the SCTP module gate means only a subset of high-value-role hosts are reachable. The floor is HIGH because the chain succeeds on ≥1% of high-value-role deployments (KVM/k8s nodes on non-RHEL distros). Not CRITICAL because the SCTP module dependency prevents universal reachability across the installed base, and the primary path requires local access.
Why not higher?
A CRITICAL rating would require either confirmed in-the-wild exploitation, universal reachability without prerequisites, or a remote unauthenticated attack path. DiagSpill fails all three: it is not KEV-listed, it requires the SCTP module (blacklisted on RHEL 8+ and hardened builds), and the remote path depends on non-default ASCONF options that essentially never exist in production. The LPE reliability is also environment-dependent, with kernel panic being the more common outcome than clean root on first attempt.
Why not lower?
A MEDIUM rating would underweight the kernel-level impact. The 8 MiB OOB write is an exceptionally powerful corruption primitive — it is not a narrow single-byte off-by-one. Public PoCs exist and demonstrate root under specific conditions. The SCTP module auto-loads on stock Ubuntu, Debian, and Fedora, which collectively represent a significant share of enterprise Linux. Any host where the chain succeeds yields root with no capabilities or namespaces required, and the deployment-role floor (KVM, k8s) keeps this firmly at HIGH.
Crowdsourced verification payload.
Run this script on each target Linux host as any user (no root required for version checks; root recommended for module-status checks). Invoke with: chmod +x diagspill_check.sh && sudo ./diagspill_check.sh. The script checks the running kernel version against known fixed releases and verifies SCTP module availability.
#!/bin/bash
# diagspill_check.sh — CVE-2026-74469 (DiagSpill) detection
# Checks: (1) kernel version against fixed releases, (2) SCTP module status
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -euo pipefail
RESULT="UNKNOWN"
KVER=$(uname -r)
KMAJOR=$(echo "$KVER" | cut -d. -f1)
KMINOR=$(echo "$KVER" | cut -d. -f2)
KPATCH=$(echo "$KVER" | cut -d. -f3 | cut -d- -f1)
echo "[*] CVE-2026-74469 (DiagSpill) check"
echo "[*] Running kernel: $KVER"
# Fixed versions: 5.10.265, 5.15.216, 6.1.183, 6.6.151, 6.12.103, 6.18.44, 7.1.8, 7.2+
version_gte() {
# Returns 0 if $1.$2.$3 >= $4.$5.$6
local a1=$1 a2=$2 a3=$3 b1=$4 b2=$5 b3=$6
if [ "$a1" -gt "$b1" ] 2>/dev/null; then return 0; fi
if [ "$a1" -lt "$b1" ] 2>/dev/null; then return 1; fi
if [ "$a2" -gt "$b2" ] 2>/dev/null; then return 0; fi
if [ "$a2" -lt "$b2" ] 2>/dev/null; then return 1; fi
if [ "$a3" -ge "$b3" ] 2>/dev/null; then return 0; fi
return 1
}
check_version() {
# Kernel < 4.7 is not affected (bug introduced in 4.7)
if [ "$KMAJOR" -lt 4 ] 2>/dev/null; then
echo "[+] Kernel predates vulnerable code (introduced in 4.7)"
RESULT="PATCHED"; return
fi
if [ "$KMAJOR" -eq 4 ] && [ "$KMINOR" -lt 7 ] 2>/dev/null; then
echo "[+] Kernel predates vulnerable code (introduced in 4.7)"
RESULT="PATCHED"; return
fi
# Check against fixed stable branches
if [ "$KMAJOR" -eq 5 ] && [ "$KMINOR" -eq 10 ]; then
if version_gte $KMAJOR $KMINOR $KPATCH 5 10 265; then
RESULT="PATCHED"; echo "[+] Fixed in 5.10.265, running $KVER"; return
fi
elif [ "$KMAJOR" -eq 5 ] && [ "$KMINOR" -eq 15 ]; then
if version_gte $KMAJOR $KMINOR $KPATCH 5 15 216; then
RESULT="PATCHED"; echo "[+] Fixed in 5.15.216, running $KVER"; return
fi
elif [ "$KMAJOR" -eq 6 ] && [ "$KMINOR" -eq 1 ]; then
if version_gte $KMAJOR $KMINOR $KPATCH 6 1 183; then
RESULT="PATCHED"; echo "[+] Fixed in 6.1.183, running $KVER"; return
fi
elif [ "$KMAJOR" -eq 6 ] && [ "$KMINOR" -eq 6 ]; then
if version_gte $KMAJOR $KMINOR $KPATCH 6 6 151; then
RESULT="PATCHED"; echo "[+] Fixed in 6.6.151, running $KVER"; return
fi
elif [ "$KMAJOR" -eq 6 ] && [ "$KMINOR" -eq 12 ]; then
if version_gte $KMAJOR $KMINOR $KPATCH 6 12 103; then
RESULT="PATCHED"; echo "[+] Fixed in 6.12.103, running $KVER"; return
fi
elif [ "$KMAJOR" -eq 6 ] && [ "$KMINOR" -eq 18 ]; then
if version_gte $KMAJOR $KMINOR $KPATCH 6 18 44; then
RESULT="PATCHED"; echo "[+] Fixed in 6.18.44, running $KVER"; return
fi
elif [ "$KMAJOR" -eq 7 ] && [ "$KMINOR" -eq 1 ]; then
if version_gte $KMAJOR $KMINOR $KPATCH 7 1 8; then
RESULT="PATCHED"; echo "[+] Fixed in 7.1.8, running $KVER"; return
fi
elif [ "$KMAJOR" -ge 8 ] 2>/dev/null; then
RESULT="PATCHED"; echo "[+] Kernel $KVER is newer than all affected branches"; return
elif [ "$KMAJOR" -eq 7 ] && [ "$KMINOR" -ge 2 ] 2>/dev/null; then
RESULT="PATCHED"; echo "[+] Kernel $KVER is >= 7.2 (fixed in 7.2-rc6)"; return
fi
# If we're in an affected major.minor but below the fix
RESULT="VULNERABLE"
echo "[-] Kernel $KVER appears VULNERABLE (below fix for this branch)"
}
check_version
# Check SCTP module status
echo ""
echo "[*] SCTP module status:"
if lsmod 2>/dev/null | grep -q '^sctp '; then
echo "[-] sctp module is LOADED — attack surface is active"
elif modprobe -n sctp 2>/dev/null; then
echo "[!] sctp module is NOT loaded but CAN be auto-loaded"
else
echo "[+] sctp module is BLOCKED or unavailable — attack surface eliminated"
if [ "$RESULT" = "VULNERABLE" ]; then
echo "[*] Kernel is vulnerable but SCTP is blocked — effective risk is mitigated"
fi
fi
if lsmod 2>/dev/null | grep -q '^sctp_diag '; then
echo "[-] sctp_diag module is LOADED"
elif modprobe -n sctp_diag 2>/dev/null; then
echo "[!] sctp_diag module can be auto-loaded"
else
echo "[+] sctp_diag module is BLOCKED or unavailable"
fi
echo ""
echo "=== RESULT: $RESULT ==="
case "$RESULT" in
PATCHED) exit 0 ;;
VULNERABLE) exit 1 ;;
*) exit 2 ;;
esac