
DaemonSet для митигации уязвимости CVE-2026-64564 (SCTPhantom)
Automatic application of mitigation for the CVE-2026-64564 (SCTPhantom) vulnerability in the Linux kernel on all worker nodes of a Yandex Managed Kubernetes cluster.
CVE identifier (CVE ID): CVE-2026-64564
CVE link: https://nvd.nist.gov/vuln/detail/CVE-2026-64564
Original report:
9b2854f86f0b in net/sctp/sm_make_chunk.cBrief description:
SCTPhantom is a use-after-free in the SCTP Dynamic Address Reconfiguration (ASCONF, RFC 5061) subsystem of the Linux kernel that allows an unprivileged local user to gain superuser (root) privileges.
The root cause is an identity mismatch when processing an ASCONF chunk: the DEL-IP operation check is performed against the IPv4 packet source address (S), while further processing uses the transport selected via the Address Parameter (L). Because of this, the ordered sequence
[ Address Parameter L ] [ DEL-IP L ] [ DEL-IP 0.0.0.0 ]
passes the checks and removes transport(L), after which the wildcard operation DEL-IP 0.0.0.0 reuses the already freed pointer as a "persistent path". As a result, asoc->peer.primary_path and asoc->peer.active_path remain dangling pointers to the freed struct sctp_transport, and a subsequent call to getsockopt(SCTP_STATUS) dereferences them.
The vulnerable logic was introduced in Linux 2.6.25 (2007, commit 42e30bf3463c), meaning it has been present in the kernel for about 18 years.
Attack:
CAP_NET_ADMIN or CAP_SYS_ADMIN, works with the default seccomp profile active; ASCONF and AUTH are enabled per-socket via SCTP_ASCONF_SUPPORTED / SCTP_AUTH_SUPPORTED, so the net.sctp.addip_enable sysctl does not need to be changedcall_usermodehelper_exec(), which launches a process in the host's initial namespaceskernel panic, making detection hardercommit_creds()), without shellcode or classic ROPAffected technologies:
net/sctp subsystem (sctp module), ASCONF handling in net/sctp/sm_make_chunk.csctp_diag module (depends on sctp), used for inspecting SCTP socketsThe vulnerability is exploitable only when SCTP is available: if the sctp module is not loaded and its autoloading is blocked, the vector is unavailable.
Targets confirmed by the authors (root obtained):
| Distribution | Kernel |
|---|---|
| Research kernel | Linux 7.2-rc2 |
| OpenCloudOS-family | 6.6.119 |
| Debian 13 | 6.12.95+deb13-amd64 |
| Rocky Linux 9 / RHEL 9 | 5.14 vendor kernel (with the sctp module loaded) |
| Ubuntu 24.04 | 6.8.0-134-generic |
Fixed kernel versions:
| Branch | First fixed version | Stable fix |
|---|---|---|
| 6.6.y | 6.6.148 | fedeb4468987 |
| 6.12.y | 6.12.101 | 74e8f3e7114f |
| 6.18.y | 6.18.42 | 85aca407c560 |
| 7.1.y | 7.1.6 | d136b29bf91d |
| mainline | 7.2-rc5 | 9b2854f86f0b |
Vendor kernels may contain a backport of the fix with an older version string — the kernel version alone is not a sufficient indicator of vulnerability; rely on the vendor's advisory or sources.
Attack vector and severity according to CVSS v4.0:
Base score: 8.5 (HIGH)
Vector: CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
The DaemonSet automatically, on every worker node of the cluster:
sctp and sctp_diag are loaded and with what refcnt. The check is intentionally passive: the DaemonSet does not open an SCTP socket before installing the blacklist, so as not to trigger autoloading of the module on a node where it is not yet loaded.refcnt and live records in /proc/net/sctp/assocs and /proc/net/sctp/eps. Kubernetes itself does not use SCTP, but user workloads may declare protocol: SCTP in a Service/Pod./etc/modprobe.d/blacklist-sctp.conf with install and blacklist rules for sctp and sctp_diag.rmmod for sctp_diag, then sctp (order matters: sctp_diag depends on sctp). If live SCTP connections are found, unloading is skipped unless FORCE_APPLY=true is explicitly set.modprobe sctp is rejected, and that creating an SCTP socket (socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) no longer succeeds.The mitigation consists of two independent parts, and on some nodes only one of them is applied.
1. Blacklist (always applied, reliable). After /etc/modprobe.d/blacklist-sctp.conf is created, the sctp module can no longer be loaded — neither automatically on socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP), nor by an explicit modprobe. This closes the vector on nodes where the module has not yet been loaded (the typical state: SCTP is not used by Kubernetes and is loaded only on demand).
2. Unloading the module from memory (not always possible). If sctp is already loaded, unloading it will most often fail. On tested Yandex Managed Kubernetes nodes (Ubuntu 22.04, kernel 5.15.0-181-generic), a freshly loaded sctp module that is not used by anyone already has refcnt=6 with an empty holders list and empty /proc/net/sctp/{assocs,eps}, and rmmod returns ERROR: Module sctp is in use. The counter does not decrease over time.
Hence two implications:
refcnt is not an indicator that SCTP is in use — the DaemonSet outputs it for informational purposes only, and decides whether to unload based on live records in /proc/net/sctp/assocs and /proc/net/sctp/eps.sctp is already resident, the DaemonSet honestly reports ⚠ mitigation applied PARTIALLY. The blacklist is in place there (the module will not return after a reboot), but until reboot the node remains vulnerable. To fully close the vector, such nodes need to be rebooted or the node group recreated.To find such nodes after rollout:
kubectl logs -n kube-system -l app=cve-2026-64564-fix -c apply-fix --prefix | grep -E 'PARTIALLY|STILL LOADED'
Important: the mitigation completely disables SCTP on the node.
Kubernetes and network plugins (Cilium, Calico) do not use SCTP for their own operation, so for the vast majority of clusters the mitigation is safe. However, if the cluster has workloads that use SCTP (for example, telecom applications, VoIP/SS7/Diameter signaling, Service or NetworkPolicy with protocol: SCTP), their traffic will stop working.
Check whether such objects exist in the cluster before rollout:
# Service / Pod с protocol: SCTP
kubectl get svc -A -o json | jq -r '.items[] | select(.spec.ports[]?.protocol=="SCTP") | "\(.metadata.namespace)/\(.metadata.name)"'
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[].ports[]?.protocol=="SCTP") | "\(.metadata.namespace)/\(.metadata.name)"'