Six machine instructions. The six-instruction race window – and Jaeyoung Chung of the Seoul National University Computer Security Lab turns the unvited process into root with 99 out of 100 attempts. Exploit published on oss-sec, bugs are registered as CVE-2026-46242 (Bad Epoll) Use-after-free in fs/eventpoll.c, CVSS 7.8 HIGH, vector AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-416. And here's what's unpleasant: the bug sits in a subsystem that can't be turned off. On epoll tied all I/O-mulplaying GNU/Linux - nginx, Node.js, Android event loop. The exploit is filed as 0-day in the Google kernelCTF (program with a reward of $71 337 per kernel exploit). Below - root cause from the patch, the chain of operation to ROP-chain with root shell, the affected versions and exposure check - a full cycle from the analysis of the patch to the working sloach disassembled in CVE Exploiting Guidelines.
Why an attacker local privilege escalation in the kernel of Linux
Privilege escalation through the vulnerability of the nucleus - Exploitation for Privilege Escalation (T1068) MITRE ATT&CK is one of the most valuable primitives in the attack chain. Bad Epoll does not give initial access: the vulnerability is local, you need a pre-execution of the code on the target host. But this is the role: to convert a limited foothold into complete compromising.
Place in kill chain:
• Before Bad Epol: the attacker received local code execution - through trojanized NPm/pip-package stolen SSH key, RCE in CI/CD-task, browser in a renderer or shell in a container
• Bad Epoll (T1068): ):unprivileged code -> root
• After Bad Epoll: persistence through kernel modules (T1547.006), rootkit (T1014), credential access, lateral infrastructure movement
[Applicable: internal pentest, post-compromise LPE, modern Linux kernel 6.4+. Not applicable: external pentest without initial access, kernel until v6.4.]
Three properties make Bad Epolll more dangerous than the typical Linux kernel privilege escalation.
No kill switch. Epoll is the basic nucleus subsystem. Copy Fail (CVE-2026-31431) can be neutralized by unloading of a vulnerable module - with epoll, such a focus will not pass. It employs OS, network services and browsers. The only way is the patch.
It works from Chrome renderer sandbox. According to the researcher, Bad Epoll is rigging from under the renderer sandbox Chrome - the same sandbox that blocks most kernel-bugs. The logginger exploit + Bad Epoll gives a kernel code execution - an analogue of the chain demonstrated by Project Zero.
Candidate for rooting Android. Of the approximately 130 vulnerabilities exploited on the Google kernelCTF, only about ten are suitable for root Android. Bad Epoll is one of them. On the Pixel 10 (core v6.6+) of the UAF is confirmed, full linux exploit root in development.
Subsystem popell: why lifetime objects becomes attack surface
Epoll is a scalable I/O multiplaying mechanism. The program creates a copy through epoll_create1(), adds the observed file descriptors through epoll_ctl()Awaiting events through epoll_wait(). On epoll built event loops's nginx, Node.js, Python asyncio, Go runtime, databases and Android.
For a simple API, a dense network of dependencies between kernel objects is hidden. Copy of popll - by itself struct file. Observed descriptors also refer to struct file - sockets, pipe, eventfd, timefrd, other epell-copies. The last case – epoll-watches-epoll - the legitimate part of the API, but it turns the management of the lifetime into a minefield: one object of notification depends on the other, and both can be closed competitively by different streams.
The mental model here is as follows: epoll is not only an event API, but a link graph between kernel-objects. The bug occurs when the rib in the graph is removed earlier than necessary, and another stream considers the graph valid and continues to walk on it. The classic problem of management, which in the nucleus turns into an exploited primitive.
Root call CVE-2026-46242: use-after-free in epoll at competitive closeness
The Vulnerable Commit - 58c9b016e128 from April 8, 2023 - reduced the content by ep_mutex in the subsystem of epoll. The same committee introduced two separate race condition in about 2500 lines of eventpoll code.
Mechanics of the race
Description of NVD indicates a problem with surgical accuracy: ep_remove() through ep_remove_file() cleaned file->f_ep under file->f_lock, but continued to use @file in the subsequent critical section. This created a window in which a competitive __fput() could have seen the transit NULL, miss eventpoll_release_file() and go to f_op->release / file_free().
What happens when two related epell descriptors are competitively closed:
1. Thread A calls ep_remove(). Under file->f_lockcleans file->f_ep = NULL.
2. Thread B Competitively Calls __fput()on the same struct file. Sees f_ep == NULL-> considers the file not related to epoll -> eventpoll_release_file()-> calls f_op->release/ file_free().
3. Thread A continues to work: is_file_epoll(), hlist_del_rcu()through epi->fllink.pprev, spin_unlock()- all at the already liberated objects.
The result is two types of epoll use-after-free:
• struct eventpollUAF: in the script epoll-watches-epopol hlist_del_rcu()writes through the pointer to the already released kmalloc-192
• struct fileUAF: struct fileuses SLAB_TYPESAFE_BY_RCU(https://www.kernel.org/doc/html/lmet/mm/slub.htm) - the slot can be reused by the allocator while the function still holds the pointer on an object that is no longer "the "the same"
Fix and kinship CVE-2026-43074
Correction - Commit a6dc643c6931 from April 24, 2026 - captures the reference to @file through epi_fget() at the beginning ep_remove(), before the association is cleansed:
C:
static void ep_remove(struct eventpoll *ep, struct epitem *epi)
{
struct file *file __free(fput) = NULL;
ep_unregister_pollwait(ep, epi);
if (unlikely(READ_ONCE(epi->dying)))
return;
file = epi_fget(epi); /* pin file f_ep */
if (!file)
return;
spin_lock(&file->f_lock);
ep_remove_file(ep, epi, file);
}
epi_fget() - the whole point. With him struct file cannot be released until ep_remove() will not finish the work. Conceptually simple - but in the nucleus "simple" fixing the link requires a neat placement: too late - the race is preserved; on the wrong object - one class of the UAF closes, the other remains; with a violation of order locking -dack.
The same comit of 2023 CVE-2026-43074 (CWE-401, CVSS 7.8) - another race in epoll. It was discovered by the AI model Anthropic Mythos, and this in itself is remarkable: race-bugs are considered one of the most difficult classes for automatic search. Fix CVE-2026-43074 postponed release struct eventpoll through the RCU grace period. But the UAF on struct file - Bad Epoll - Remained: Removal of one symptom did not close the entire life-problem in the design.
Why did Mythos miss Bad Epill? The researcher calls two probable reasons. The race window is six instructions; the exact interleaving threads is difficult to visualize even a person, looking at the source. And after fixing CVE-2026-43074 Bad Epol not triggeritis KASAN - main runtime detector memory errors core. Without this signal, the model could not have enough confidence for the report. The first patch of continents also did not completely close the problem - the correct fix appeared only two months after the initial report.
Exploitation of Linux kernel vulnerability: from UAF to root shell
Adjustments to the environment for reproduction:
• Linux kernel 6.4 - 6.12.94 (or 6.6 - 6.6.143 for LTS-four) without fix a6dc643c6931
• Local code execution from the unvitiled user
• C++ compiler (exploit written in C++)
• For safe testing: QEMU + pwndbg/gdb, 2+ GB RAM for VM
The public Bad Epoll repository is a complete write-up code showing the way from a six-instructive window to a reliable LPE linux.
High-level operation chain bad epoll exploit:
1. Four popll objects are created and grouped into two pairs of epoll-watches-eplo. One pair is a trigger of the race, the other is a victim.
2. Competitive closure of the trigger pair provokes close-vs-close race. The exploit expands the window through timer interrupts and triggers a retry cycle that does not map the nucleus when failures. This is fundamental - you can crush the race at least a thousand times without panic.
3. 8-byte UAF-recording in the released kmalloc-192through hlist_del_rcu()which writes through epi->fllink.pprev.
4. Pivot on struct fileUAF - cross-cache attack gives full control over the contents of the file object.
5. Arbitrary kernel read via /proc/self/fdinfo- tangle struct file, substituted under the pipe, leaks the kernel-address. Bypass KASLR without a separate info burst.
6. Hijack control flow - intercepting indirect call, capture of instruction pointer.
7. ROP-chain -> privileges induced -> root shell.
Confirmed reliability for Google kernelCTF:
• lts-6.12.67 - 99% reliability
• cos-121-18867.294.100 (Container-Optimized OS) - 98% reliability
• Pixel 10 (core v6.6+) - UAF confirmed full electrocrine in development. The Pixel 8 and the devices on the v6.1 kernel are not affected.
Restrictions of operation
Not remote: Prerequisite prior code execution. Without a foothold on the Bad Epoll host is useless.
Kernel CFI (CONFIG_CFI_CLANG): Complicates ROP, but does not block the UAF Primitive. The use-after-free and read itself work independently of CFI.
seccomp with hard whitelist: theoretically can block popll-syscols, in practice epoll is allowed almost everywhere - too much software depends on it.
grsecurity/PaX (RANDKSTACK, etc.): complicates the heap spray and the layout prediction, does not eliminate root cause. It makes it difficult to operate, but does not make it impossible.
Smoked nuclei: 6.6.144+ and 6.12.95+ contain a fix - exploit does not work.
Why an attacker local privilege escalation in the kernel of Linux
Privilege escalation through the vulnerability of the nucleus - Exploitation for Privilege Escalation (T1068) MITRE ATT&CK is one of the most valuable primitives in the attack chain. Bad Epoll does not give initial access: the vulnerability is local, you need a pre-execution of the code on the target host. But this is the role: to convert a limited foothold into complete compromising.
Place in kill chain:
• Before Bad Epol: the attacker received local code execution - through trojanized NPm/pip-package stolen SSH key, RCE in CI/CD-task, browser in a renderer or shell in a container
• Bad Epoll (T1068): ):unprivileged code -> root
• After Bad Epoll: persistence through kernel modules (T1547.006), rootkit (T1014), credential access, lateral infrastructure movement
[Applicable: internal pentest, post-compromise LPE, modern Linux kernel 6.4+. Not applicable: external pentest without initial access, kernel until v6.4.]
Three properties make Bad Epolll more dangerous than the typical Linux kernel privilege escalation.
No kill switch. Epoll is the basic nucleus subsystem. Copy Fail (CVE-2026-31431) can be neutralized by unloading of a vulnerable module - with epoll, such a focus will not pass. It employs OS, network services and browsers. The only way is the patch.
It works from Chrome renderer sandbox. According to the researcher, Bad Epoll is rigging from under the renderer sandbox Chrome - the same sandbox that blocks most kernel-bugs. The logginger exploit + Bad Epoll gives a kernel code execution - an analogue of the chain demonstrated by Project Zero.
Candidate for rooting Android. Of the approximately 130 vulnerabilities exploited on the Google kernelCTF, only about ten are suitable for root Android. Bad Epoll is one of them. On the Pixel 10 (core v6.6+) of the UAF is confirmed, full linux exploit root in development.
Subsystem popell: why lifetime objects becomes attack surface
Epoll is a scalable I/O multiplaying mechanism. The program creates a copy through epoll_create1(), adds the observed file descriptors through epoll_ctl()Awaiting events through epoll_wait(). On epoll built event loops's nginx, Node.js, Python asyncio, Go runtime, databases and Android.
For a simple API, a dense network of dependencies between kernel objects is hidden. Copy of popll - by itself struct file. Observed descriptors also refer to struct file - sockets, pipe, eventfd, timefrd, other epell-copies. The last case – epoll-watches-epoll - the legitimate part of the API, but it turns the management of the lifetime into a minefield: one object of notification depends on the other, and both can be closed competitively by different streams.
The mental model here is as follows: epoll is not only an event API, but a link graph between kernel-objects. The bug occurs when the rib in the graph is removed earlier than necessary, and another stream considers the graph valid and continues to walk on it. The classic problem of management, which in the nucleus turns into an exploited primitive.
Root call CVE-2026-46242: use-after-free in epoll at competitive closeness
The Vulnerable Commit - 58c9b016e128 from April 8, 2023 - reduced the content by ep_mutex in the subsystem of epoll. The same committee introduced two separate race condition in about 2500 lines of eventpoll code.
Mechanics of the race
Description of NVD indicates a problem with surgical accuracy: ep_remove() through ep_remove_file() cleaned file->f_ep under file->f_lock, but continued to use @file in the subsequent critical section. This created a window in which a competitive __fput() could have seen the transit NULL, miss eventpoll_release_file() and go to f_op->release / file_free().
What happens when two related epell descriptors are competitively closed:
1. Thread A calls ep_remove(). Under file->f_lockcleans file->f_ep = NULL.
2. Thread B Competitively Calls __fput()on the same struct file. Sees f_ep == NULL-> considers the file not related to epoll -> eventpoll_release_file()-> calls f_op->release/ file_free().
3. Thread A continues to work: is_file_epoll(), hlist_del_rcu()through epi->fllink.pprev, spin_unlock()- all at the already liberated objects.
The result is two types of epoll use-after-free:
• struct eventpollUAF: in the script epoll-watches-epopol hlist_del_rcu()writes through the pointer to the already released kmalloc-192
• struct fileUAF: struct fileuses SLAB_TYPESAFE_BY_RCU(https://www.kernel.org/doc/html/lmet/mm/slub.htm) - the slot can be reused by the allocator while the function still holds the pointer on an object that is no longer "the "the same"
Fix and kinship CVE-2026-43074
Correction - Commit a6dc643c6931 from April 24, 2026 - captures the reference to @file through epi_fget() at the beginning ep_remove(), before the association is cleansed:
C:
static void ep_remove(struct eventpoll *ep, struct epitem *epi)
{
struct file *file __free(fput) = NULL;
ep_unregister_pollwait(ep, epi);
if (unlikely(READ_ONCE(epi->dying)))
return;
file = epi_fget(epi); /* pin file f_ep */
if (!file)
return;
spin_lock(&file->f_lock);
ep_remove_file(ep, epi, file);
}
epi_fget() - the whole point. With him struct file cannot be released until ep_remove() will not finish the work. Conceptually simple - but in the nucleus "simple" fixing the link requires a neat placement: too late - the race is preserved; on the wrong object - one class of the UAF closes, the other remains; with a violation of order locking -dack.
The same comit of 2023 CVE-2026-43074 (CWE-401, CVSS 7.8) - another race in epoll. It was discovered by the AI model Anthropic Mythos, and this in itself is remarkable: race-bugs are considered one of the most difficult classes for automatic search. Fix CVE-2026-43074 postponed release struct eventpoll through the RCU grace period. But the UAF on struct file - Bad Epoll - Remained: Removal of one symptom did not close the entire life-problem in the design.
Why did Mythos miss Bad Epill? The researcher calls two probable reasons. The race window is six instructions; the exact interleaving threads is difficult to visualize even a person, looking at the source. And after fixing CVE-2026-43074 Bad Epol not triggeritis KASAN - main runtime detector memory errors core. Without this signal, the model could not have enough confidence for the report. The first patch of continents also did not completely close the problem - the correct fix appeared only two months after the initial report.
Exploitation of Linux kernel vulnerability: from UAF to root shell
Adjustments to the environment for reproduction:
• Linux kernel 6.4 - 6.12.94 (or 6.6 - 6.6.143 for LTS-four) without fix a6dc643c6931
• Local code execution from the unvitiled user
• C++ compiler (exploit written in C++)
• For safe testing: QEMU + pwndbg/gdb, 2+ GB RAM for VM
The public Bad Epoll repository is a complete write-up code showing the way from a six-instructive window to a reliable LPE linux.
High-level operation chain bad epoll exploit:
1. Four popll objects are created and grouped into two pairs of epoll-watches-eplo. One pair is a trigger of the race, the other is a victim.
2. Competitive closure of the trigger pair provokes close-vs-close race. The exploit expands the window through timer interrupts and triggers a retry cycle that does not map the nucleus when failures. This is fundamental - you can crush the race at least a thousand times without panic.
3. 8-byte UAF-recording in the released kmalloc-192through hlist_del_rcu()which writes through epi->fllink.pprev.
4. Pivot on struct fileUAF - cross-cache attack gives full control over the contents of the file object.
5. Arbitrary kernel read via /proc/self/fdinfo- tangle struct file, substituted under the pipe, leaks the kernel-address. Bypass KASLR without a separate info burst.
6. Hijack control flow - intercepting indirect call, capture of instruction pointer.
7. ROP-chain -> privileges induced -> root shell.
Confirmed reliability for Google kernelCTF:
• lts-6.12.67 - 99% reliability
• cos-121-18867.294.100 (Container-Optimized OS) - 98% reliability
• Pixel 10 (core v6.6+) - UAF confirmed full electrocrine in development. The Pixel 8 and the devices on the v6.1 kernel are not affected.
Restrictions of operation
Not remote: Prerequisite prior code execution. Without a foothold on the Bad Epoll host is useless.
Kernel CFI (CONFIG_CFI_CLANG): Complicates ROP, but does not block the UAF Primitive. The use-after-free and read itself work independently of CFI.
seccomp with hard whitelist: theoretically can block popll-syscols, in practice epoll is allowed almost everywhere - too much software depends on it.
grsecurity/PaX (RANDKSTACK, etc.): complicates the heap spray and the layout prediction, does not eliminate root cause. It makes it difficult to operate, but does not make it impossible.
Smoked nuclei: 6.6.144+ and 6.12.95+ contain a fix - exploit does not work.