Four Linux kernel flaws that let an ordinary user on a machine take it over as root went public on the same morning. At 06:15 UTC on 18 September 2026, researcher Asim Viladi Oglu Manizada posted to the oss-security mailing list that DirtyAH6, TUNderflow, PPPoEject and DiagSpill were public. They're CVE-2026-80844, CVE-2026-81000, CVE-2026-68121 and CVE-2026-74469. He linked write-ups and proof-of-concept code so others could check them.
The secrecy period Manizada agreed with the linux-distros@ list had already run out. He says the mistakes behind the bugs have been sitting in the Linux kernel's networking code for ten to twenty-one years. Fixes have been going into the stable kernel versions for weeks. What changed on the 18th is that everything became public: the names, the CVEs, tables of affected versions, and working attacks tuned to particular systems.

Below, we explain what root access means on a shared machine, why the bugs were kept secret first, and what each of the four actually is, with its CVE and the part of the kernel it lives in. We cover which ones need a feature called unprivileged user namespaces and which don't, where an attacker could crash a machine over the network, and where Manizada says taking it over remotely is hard or impossible. Then there's the first kernel versions that fix all four, what to do if you can't patch tonight, and how the bugs were found.
What "local root" means
Local privilege escalation, or LPE for short, means an attacker who can already run some code as an ordinary user on a machine can make themselves root. Root is the account that controls the kernel and the whole system. That "already has some access" part matters. By default, these aren't bugs where anyone on the internet becomes root. They're bugs where someone who can log in, or who's already broken into a low-level service account, can finish the job.
On a laptop only you use, that still matters if malware gets in under your account. On a server with lots of users, a shared university machine, a CI runner that builds code, or a machine hosting containers, it matters more. Root isn't just a title. It lets you read every secret the machine holds, rewrite the files that control trust, and stay hidden there.
Manizada's oss-security post is blunt about the impact. All four proof-of-concepts take an ordinary local user to running code as root on the systems they target. The attack code is tuned to particular setups, and the notes on each repository spell out which distro, which kernel and what memory setup they assume. He warns they can corrupt the wrong memory and should only be run on spare machines you don't mind breaking. That's a warning for people testing their patches, not an invitation to try it on live systems.
How the secrecy period worked
Manizada says he reported the four bugs to security@kernel.org and the right maintainers in mid-July 2026. Fixes went in over the following weeks. He agreed an embargo, a secrecy period, with the linux-distros@ list, so Linux distributions could ship patches before he published the details and attack code. The agreed time to go public was 6am UTC on 18 September 2026.
That's the dull but important process behind the flashy names. Find the bug, report it, patch it, wait, then publish. The day it went public isn't the day the bugs appeared. It's the day the rest of us were allowed to talk about them with all the technical details out.
In his write-up he thanks the maintainers who helped patch and coordinate, including Stefan Klassert, Xin Long, Paolo Abeni, Willem de Bruijn, Greg Kroah-Hartman and others.
The four bugs at a glance
DirtyAH6 is CVE-2026-80844. It's in the IPsec Authentication Header code for IPv6, known as AH6, which uses the kernel's XFRM code. The fix's commit message in the public code is “xfrm: ah6: validate routing header segments_left” (7bad4bda74dc4713f398d3b7624ff05478e3a568).
TUNderflow is CVE-2026-81000. It's in TUN and TAP, the kernel's virtual network devices. The fix is “net: tun: bound receive headroom” (447c9303942c439a117d9b76ce6d6e2116b38ee7).
PPPoEject is CVE-2026-68121. It's in the code that sends PPPoE traffic. The fix is “pppoe: reload header pointer after dev_hard_header()” (e9c238f6fe42fb1b4dba3a578277de32cb487937).
DiagSpill is CVE-2026-74469. It's in the SCTP diagnostics code, sock_diag and sctp_diag. The fix is “sctp: prevent peer transport count overflow” (bd0e9289e2642f6a5c54faad304ce0f41e926d22).
Manizada says the mistakes behind them are ten to twenty-one years old, depending on the bug. He isn't saying attacks like today's have worked for two decades. He's saying the unsafe code has been there that long.

DirtyAH6: trusting a routing header count too much
Here's the background, the way Manizada explains it. IPsec's Authentication Header checks that a packet hasn't been changed on the way. Linux handles the IPv6 side in AH6, using the kernel's XFRM code. Before it works out or checks the authentication data, AH6 shuffles some IPv6 fields into an expected order, including the addresses inside a routing header.
The bug is in a function called ipv6_rearrange_rthdr(). It took the number of addresses from hdrlen, then used segments minus segments_left to move a pointer to an address, without first checking that segments_left was less than or equal to segments. A raw IPv6 HDRINCL packet with hdrlen=2 and segments_left=255 moved the pointer back 4,064 bytes and passed a length of 4,064 bytes to memmove(). That reached memory outside where it should, which is called an out-of-bounds access.
The fix, as the write-up sketches it, is to check segments_left before rearranging anything, and to return -EINVAL through AH6's existing error paths if the count is impossible.
To trigger the memory corruption locally, the oss-security post says you need AH6 and XFRM support. You also need unprivileged user and network namespaces, or CAP_NET_ADMIN and CAP_NET_RAW over a network namespace the attacker controls. Manizada's footnote matters here. Unprivileged user namespaces are the usual way in for an ordinary user, but they're not the only one. A container with the right capabilities can hit the corruption without creating new user namespaces.
Now the remote side, carefully. If the target works as an IPv6 router or gateway and adds AH in transport mode, the bug can be used to crash it from the network, which is a denial of service. Manizada says that by arranging memory on the target machine itself, he turned it into remote root in a lab. Doing that purely from the network, he writes, looks extremely difficult. It's possible in theory, but he hasn't shown it's easy.
The write-up says the DirtyAH6 attack is related to the earlier Dirty Frag ESP technique. It corrupts skb_shared_info through the AH6 routing-header memmove bug, then sets up a later ESP decrypt write into a file-backed fragment so pam_rootok.so gets replaced with pam_permit.so. After that, su gives a root shell. That's the outline he published. We won't give step-by-step instructions.
TUNderflow: headroom that wraps the wrong way
TUN and TAP are virtual network devices that pass packets between the kernel and ordinary programs through /dev/net/tun. When network devices are stacked on each other, they can pass down how much spare space, or headroom, they need at the front of each packet, using ndo_set_rx_headroom(). Open vSwitch can carry that number from one port to a TUN or TAP port.
Here's the bug. tun_set_headroom() stored the headroom straight into tun->align, and tun_get_user() also used that number to decide how much packet data to keep at the front. A netkit device set up with 4,096 bytes of headroom, under VXLAN and Open vSwitch, could pass 4,160 bytes to a raw TUN port. SKB_MAX_HEAD(4160) then went below zero. The negative good_linear value turned into a huge positive size_t. prepad + linear and len - linear wrapped around. tun_alloc_skb() left skb->data 64 bytes past the end of its 4,096-byte memory block. Later packet handling then read and wrote outside that block.
The fix limits the headroom TUN stores to what fits in one page for the packet head, and to the biggest 16-bit header offset allowed. It leaves enough room for the raw TUN protocol byte or a full TAP Ethernet header, and checks those bytes are there before using them.
To trigger it locally, you need TUN support and some network device path that can pass along too much headroom. You also need unprivileged user and network namespaces, or CAP_NET_ADMIN over a network namespace the attacker controls.
Manizada doesn't describe any way to trigger TUNderflow remotely.
In outline, the attack puts file-backed pipe buffers next to the bad TUN packet. An out-of-bounds write in Open vSwitch sets PIPE_BUF_FLAG_CAN_MERGE on one of them. A pipe write then replaces pam_rootok.so with pam_permit.so, and su gives root again. The same testing-only warning applies, and we won't give instructions.
PPPoEject: a pointer that outlived its buffer
PPPoE carries PPP sessions inside Ethernet frames. Internet connections often use it. When sending, pppoe_sendmsg() builds a packet buffer, called an skb, copies in the data, and asks the network device underneath to create its hardware header before filling in the PPPoE header.
Here's the bug. pppoe_sendmsg() held onto a pointer into the skb head across the call to dev_hard_header(), even though a device callback could call pskb_expand_head() and free that memory. Stalling the data copy on FUSE while adding the first GRE or IP6GRE port to an empty team or bonding device set off the reallocation. That effectively threw out the old skb head while PPPoE still pointed to it, which is where the name comes from. Later writes of the header and length used that stale pointer. It's a classic use-after-free bug, in networking code.
The fix reloads the PPPoE header through the skb's network header offset after the device header is created, and makes sure pskb_expand_head() updates that offset when it moves the head.
To trigger it locally, you need PPPoE support and a device header callback underneath that can reallocate the skb head during dev_hard_header(). You also need unprivileged user and network namespaces, or CAP_NET_ADMIN over a network namespace the attacker controls.
The original post doesn't describe a way to use it remotely.
In outline, the attack races to fill the freed skb head with file descriptor tables. The PPPoE writes point a live entry at a fake file object the attacker built. Closing that file calls a kernel function the attacker controls, which installs root credentials and opens a root shell. Again, it's published so people can test patches on spare machines, and we won't give instructions.

DiagSpill: when 65,536 wraps a 16-bit counter
An SCTP connection, called an association, can have lots of peer transports, one for each address the other side has. sctp_diag reports information about SCTP sockets and their peers through sock_diag, building a Netlink reply with one sockaddr_storage for each transport.
Here's the bug. An association can have 65,536 peer transports, but transport_count is only 16 bits wide, so it can only count to 65,535. The 65,536th transport rolled the counter back to zero. sctp_diag then set aside no room for peer data but copied the whole list anyway, spilling about 8 MiB past the end of the Netlink reply.
The fix rejects a new unique peer once transport_count reaches U16_MAX. The check comes after looking for an existing peer, so an address that's already on the list still gets its existing transport at the limit.
To trigger it locally, you need SCTP and sctp_diag support. As Manizada describes it, you don't need unprivileged user namespaces or any special capability. That makes DiagSpill the odd one out. If those modules are available, the attack doesn't need the namespace trick the other three rely on for ordinary users.
On the remote side, ASCONF and ADD-IP have to be turned on, along with either SCTP-AUTH or net.sctp.addip_noauth_enable=1. All of those are off by default. If they're on, a malicious peer can add enough transports to roll transport_count over. Something on the target, like the ss tool, still has to send the sock_diag request that sets off the overwrite. That can crash the machine, a denial of service. Manizada says he can't see a way to full remote root, even with perfect control of memory from the network.
In outline, the attack steers the sock_diag overwrite into page tables, then uses the damaged page tables to map the host's memory. It finds and rewrites a credential object, adds a sudoers rule and opens a root shell. It's for testing only, and we won't give instructions.
Containers, security modules and the "ordinary user" catch
Manizada found that AppArmor and SELinux don't block the attacks in his testing. The exception is Ubuntu's setups that block unprivileged user namespaces outright. That's a tested claim about his attack code. It doesn't prove every security module policy in the world is useless.
He writes that all four bugs can also be used to corrupt the host kernel from inside a container. The first three would need the right capabilities, without creating new user namespaces. DiagSpill would work without any special capabilities, as long as SCTP and sctp_diag are available. In theory, that could let someone escape a container. He didn't explore that with his attack code. The write-up says he took the same approach with earlier related work such as CIFSwitch and OVSwrap.
So here's the catch. Saying the first three attacks need unprivileged user namespaces is true for the usual ordinary-user case and for the way Manizada packaged his attacks. It's no protection at all if a process already has CAP_NET_ADMIN, and CAP_NET_RAW for DirtyAH6, in the right namespace.
Which kernels were affected, and which first fix all four
Manizada publishes long tables of affected versions for each bug. For admins, the short version is simpler. Upgrade to a kernel that has all four fixes. The first upstream stable releases with the full set, listed in both the oss-security post and the write-up, are these.
5.10.270, 5.15.221, 6.1.188, 6.6.157, 6.12.109, 6.18.50 and 7.2.4.
Those are the main stable kernel numbers. Most machines run a kernel from their Linux distribution, with its own version numbers and fixes copied back in. Check your distribution's security advisory for packages that include all four. Don't assume the number uname -r shows you matches the table exactly.
Each fix landed in slightly earlier stable versions before the full set. For example, the write-up says DirtyAH6 was first fixed in 5.10.269, 5.15.220, 6.1.187, 6.6.156, 6.12.108, 6.18.49, 7.1.13 and 7.2.3, with TUNderflow, PPPoEject and DiagSpill fixed at nearby but not identical points. If you're tracking down a partial fix, use the tables for each CVE. If you're patching lots of machines tonight, aim for the releases with all four, or your distribution's matching advisory.
Some kernel lines in the tables are end-of-life, meaning they never got these fixes upstream, and they'll stay that way. Distributions that still use them need to copy the fixes back themselves or move off those versions.
If you can't patch in the next hour
Apart from updating the kernel, Manizada suggests two things you can do right away.
First, turning off unprivileged user namespaces blocks the usual ordinary-user route to the first three bugs. It doesn't protect against containers or other processes with the right capabilities, and DiagSpill can still be reached.
Second, turn off AH6, TUN, PPPoE, SCTP or sctp_diag if you don't use them.
He doesn't recommend just turning off the particular kernel modules his attack code uses, because there may be other ways to get root. Patching is better. The Hacker News, reporting on 18 September, gives the same order of steps. Patch first. Turning off namespaces and features only lowers the risk for a while. And check your distribution's advisories instead of just matching upstream version numbers.
The Hacker News also reported, as of its 18 September story, that there were no known real-world attacks using the four bugs. It noted the attacks are Manizada's own, tuned to particular builds, and can crash a machine, so they should only be run on separate test systems. That finding comes from one article on one date, and it could change.
How the four bugs were found
Manizada writes that he found them by combining two things. One is the graph-based tracking of security-relevant objects and properties he used in his earlier CIFSwitch work. The other is tooling that lets AI agents reason “geometrically” about the state of memory, as in OVSwrap. He says the setup is described in broad strokes in an earlier post about getting AI language models to find remote Linux kernel out-of-bounds writes, though it's changed since then.
His write-up ends by saying that because of some personal and work changes, this probably ends his AI-assisted bug hunting experiment, at least in public, for a while.
Other reports note that at least one of the fix commits has an Assisted-by line crediting custom AI tools.
That doesn't mean every future kernel bug will be found this way, and it isn't a review of any AI company's models.
The key points
On 18 September 2026 at 06:15 UTC, after the secrecy period agreed with linux-distros, Manizada published four Linux local root bugs, with CVEs, a write-up and attack code for testing.
DirtyAH6 is in AH6 and XFRM. TUNderflow is in the TUN and TAP headroom code. PPPoEject is a use-after-free in PPPoE. DiagSpill is an SCTP transport_count rollover that spills into sctp_diag.
The mistakes in the code are about 10 to 21 years old. Fixes went into the stable kernels over recent weeks, before the public post.
For ordinary users, the first three usually depend on unprivileged user namespaces, though a container with the right capabilities doesn't need them. DiagSpill doesn't need them at all.
Remotely, DirtyAH6 can crash an IPv6 gateway or router using AH in transport mode. Manizada got remote root in a lab by arranging memory on the target, and he calls doing it purely from the network extremely difficult. DiagSpill can only crash a machine remotely with SCTP options that are off by default, plus a local sock_diag request to set it off, and he sees no way to remote root. TUNderflow and PPPoEject aren't described as remote bugs.
The kernels that fix all four are 5.10.270, 5.15.221, 6.1.188, 6.6.157, 6.12.109, 6.18.50 and 7.2.4, or your distribution's matching advisory.
There's no official video for this disclosure.
Why networking code keeps turning up in root bugs
The kernel's networking code is a big target. It has old code, lots of optional modules and complicated rules about how long packet buffers live. Headers get rewritten. Buffers get expanded. Counters roll over. And namespaces let ordinary users borrow admin powers, so they can build containers and VPNs without a root password. Each of those design choices is useful. Each one also creates places where a missing size check or an out-of-date pointer lets someone cross a privilege line.
Look at the four bugs that way and they stop looking random. DirtyAH6 is a missing comparison on a routing header field. TUNderflow is a size that goes below zero when the headroom is too big. PPPoEject is a pointer held across a call that can free the memory under it. DiagSpill is a 16-bit counter that rolls over when the list of peers gets huge. They're in different parts of the kernel, but they're the same kind of memory safety mistake.
Namespaces are the key policy question. The features that let ordinary users act like an admin inside a sandbox also let them reach networking controls that are normally for admins. When the kernel bug is in the networking code those controls touch, the sandbox wall isn't the security wall you thought it was. DiagSpill goes further. Sometimes you don't even need the sandbox.
Reading the version tables
If you look after kernels for a living, open the tables in the write-up. Each CVE has its own range of affected versions and its own first fixed version. The list of releases with all four fixes is the overlap you want on general-purpose machines.
If you only remember one set of numbers, make it the list of stable releases with all four fixes. If you only remember one order of steps, make it this. Patch first, then think about your namespace policy, then turn off AH6, TUN, PPPoE or SCTP if you don't use them. Blocking modules on its own isn't a plan.
And if you only remember one thing about remote attacks, remember what Manizada said. Getting root remotely through DirtyAH6 purely from the network is extremely difficult, and with DiagSpill he sees no way to full remote root, even with perfect control of memory from the network.
The bottom line
The secrecy period is over and four local root bugs are public. The bugs are old, and the fixes are in the kernel code. The attack code is out for people testing patches on machines they can afford to crash. Shared machines, container hosts with the wrong modules loaded, and any machines still running kernels older than those stable releases should upgrade.
If you run machines with lots of users, CI runners or container hosts, start with the list of stable releases with all four fixes, or your distribution's advisory. If you can't patch tonight, use the temporary steps Manizada listed, remembering that turning off namespaces doesn't stop DiagSpill, and then come back and update the kernel.
The names, CVEs and the list of kernels with all four fixes are all above. Patch as soon as you can.
For more, read about a different root bug in a mail gateway, CISA's three-day patch deadlines for the worst bugs and Chromium's bug on CISA's exploited list.
Sources
oss-security, A quartet of Linux local root vulns: DirtyAH6, PPPoEject, TUNderflow, and DiagSpill (18 Sep 2026, 06:15 UTC)
Asim Manizada, A quartet of Linux local root vulns (18 Sep 2026)
Attack code repositories for testing, linked from the original post: github.com/manizada/DirtyAH6, TUNderflow, PPPoEject, DiagSpill.
The CVE numbers as assigned in the original post: CVE-2026-80844, CVE-2026-81000, CVE-2026-68121 and CVE-2026-74469.
The Hacker News, Public Exploits Released for Four Linux Kernel Flaws That Enable Local Root (18 Sep 2026).





The paper
Comments
No notes on this story yet.
Sign in to comment