Get the latest, first
arrowBlog
io_uring Is Back, This Time as a Rootkit

io_uring Is Back, This Time as a Rootkit

Apr 24, 2025

Amit Schendel
Head of Security Research

ARMO researchers reveal a major blind spot in Linux runtime security tools caused by the io_uring interface—an asynchronous I/O mechanism that bypasses traditional system calls. Most tools, including Falco,and Microsoft Defender fail to detect rootkits using io_uring because they rely heavily on syscall monitoring. In addition, Tetragon users must be well-versed about this evasion technique and define policies accordingly. ARMO’s proof-of-concept rootkit, Curing, operates fully via io_uring to demonstrate the threat. While some vendors responded with fixes or workarounds, the broader industry remains exposed. 

Intro

io_uring has been around for years since Linux 5.1, since then, it has been notorious in Linux security circles for the sheer number of vulnerabilities in this mechanism.

In this blog, we will explore how io_uring can also be used as an evasion technique that affects most Linux runtime security tools today. We will showcase ARMO’s research around this mechanism and its wide impact on the Linux security domain.

Background

Two years ago, our team at ARMO conducted research on bypassing and maneuvering eBPF-based monitoring tools. Some of the cool techniques we explored were eBPF maps tampering and TOC-TOU attacks. This research led us to an interesting blog post from June 6, 2022. In his blog, Daniel Teixeira, a Senior Red Team Operator, demonstrated how by using io_uring one can bypass syscall monitoring on Linux-based systems. We found that method very interesting, but not fully explored so we kept an eye on its evolution. At the latest Chaos Communication Congress, we decided it was time to take this “underrated” mechanism and turn it from an idea into a reality. This led us to understand how serious the situation is.

Bottom line: there is a massive security loophole in tools and policies that rely on syscall monitoring for Linux runtime security

What is io_uring, and why we care about it

TL;DR: 

This mechanism allows a user application to perform various actions without using system calls. As a result, security tools relying on system call monitoring are “blind” to rootkits working solely on io_uring.

io_uring is a Linux-specific kernel API for asynchronous I/O. It allows the user to submit one or more I/O requests, which are processed asynchronously without blocking the calling process.  io_uring gets its name from ring buffers, which are shared between user space and kernel space. This arrangement allows for efficient I/O, while avoiding the overhead of copying buffers between them, where possible. This interface makes io_uring different from other UNIX-style I/O APIs, wherein, rather than just communicating between kernel and user space with system calls, ring buffers are used as the primary mode of communication.

Source: developers.redhat.com

The creation of Curing

We decided to create Curing and release it publicly for a couple of reasons:

 First, we wanted to raise awareness about io_uring as an overlooked mechanism that attackers can exploit. Since its initial release, “io_uring” has been under scrutiny, and the Linux security community has identified numerous issues with it. So much so that Google ultimately decided to disable it by default on Android devices. Despite this level of attention, security vendors have largely failed to monitor application activity involving this feature. 

Additionally, for the past two years, there have been publications detailing how this technique can be used to bypass detection mechanisms. Yet, most cybersecurity vendors still haven’t addressed the issue.

This leads to our second reason. We wanted to highlight the lack of proper attention in designing monitoring solutions that are forward-compatible. Specifically, these solutions should be compatible with new features in the Linux kernel and address new techniques discovered by adversaries. Yet, vendors continue to rely on easy and straightforward solutions that are not immune to changes and diversity of applications and kernel features.At the time of writing this blog post, there are 61 possible operations using io_uring, which include network and file system operations. You can find the complete list here. While the risk of io_uring was discussed in theory, we decided to create a fully functioning rootkit that works solely on io_uring for all of its operations. You can find the rootkit here. The rootkit demonstrates communication between a C2 server and an infected host to pull commands and execute them without making any system calls relevant to its operations. The main idea was to show that io_uring allows so many important operations that you can write an entire rootkit on top of.

The impact

During the development of Curing and based on the experience of writing a runtime security tool for Linux ourselves, we realized the potential impact of this mechanism on other security vendors. Since, to the best of our knowledge, most of the commercial solutions in the Linux EDR scene today rely on system call hooking, we decided to put these solutions to the test against our rootkit.

We started by checking well-known open-source tools first. We selected two CNCF graduated projects, Falco and Tetragon. Both mostly focus on runtime detection, many of their policies rely on system call hooking using eBPF, in case of Falco, optionally on Kernel modules.

Open source tools

Falco

Falco, as demonstrated above, is completely blind to io_uring-based operations as it relies heavily on system call hooking. We took some of its default rules and even created some custom policies and ran Curing. Thus, confirming it does not detect the malicious operations.

We reported the issue to the Falco maintainers which were quick to respond. Luca Guerra quickly acknowledged the issue and informed us that they are working on delivering a plugin that will allow Falco users to create LSM hooks with eBPF. This solution can give deeper visibility as we will explain  in the next section.

You can find more information in the Falco GitHub repository.

Tetragon

We see Tetragon as a security and observabilty toolkit, rather than a opinionated security product. As a result, some of the sample policies and common use cases are blind to io_uring because the detections are based on system call hooking. Readers should keep in mind that Tetragon allows users to hook not only system calls, but also to Kprobes and LSM hooks, which gives deeper visibility and will catch io_uring operations. Tetragon can detect io_uring, but users need specific knowledge of what to hook and how to do it. Therefore, a thorough understanding of Linux is essential for Tetragon users.

We reported this to the Tetragon team and their response was that from their perspective Tetragon is not “vulnerable” as they provide the flexibility to hook basically anywhere. They pointed out a good blog post they shared on the subject. We highly recommend Tetragon users study and understand these caveats.

Commercial tools

After we learned how the problem manifests in open-source solutions we tested some commercial vendors too. We found that many well-known commercial products we tested showed this detection gap.

According to a senior vice president at one of the top cybersecurity companies: “We take this very seriously as it bypasses all our file system visibility ”.

Microsoft Defender

Unfortunately, Microsoft was unresponsive despite multiple attempts on our part to reach out and engage with them. Like several other security products we evaluated, Microsoft Defender for Endpoint on Linux currently lacks comprehensive visibility into various types of threats. We enabled endpoint protection and tried a range of techniques—including reading sensitive files, dropping the EICAR test malware, executing the XMRig crypto miner, and reaching out to a low-reputation network endpoint—but none of these actions were detected, regardless of whether io_uring was used.

The only detection we managed to trigger was through the File Integrity Monitoring (FIM) module, which monitors specific paths for changes. In this case, Defender detected file modifications both with and without io_uring, we believe this could be because of Fanotify hooks.However, upon examining the machine, we observed that many eBPF hooks were deployed, most of which monitor system calls—including those related to networking. This leads us to believe that even if Microsoft were to add detection capabilities for other types of system activity, these could likely still be bypassed using io_uring. Therefore, we strongly recommend that Microsoft take action and rethink their detection strategy.

SentinelOne

We also reached out to SentinelOne, who confirmed that their agent is not affected by this. Special thanks to Dor from the Cloud Security team for taking this seriously and testing it with us.

Sadly, some of the vendors we contacted were unresponsive and due to the rules of ethical hacking, we weren’t able to test them or publish our findings on the ones we did test. However, based on all the findings we managed to gather , we have reason to believe many other commercial solutions are impacted. If you would like to test a solution, you can use this PoC.

The state of Linux runtime security

Today, many security vendors are shifting towards building eBPF-based agents, largely because eBPF is considered “safe” for use in products like EDR and CWPP. However, working with eBPF comes with inherent challenges and constraints, particularly due to its verifier, which imposes strict limitations on what code can be safely loaded. This makes the placement of hooks a critical decision.

On the one hand, you need visibility into system calls; on the other, you need access to kernel structures and sufficient context to detect threats effectively. Many vendors take the most straightforward path: hooking directly into system calls. While this approach offers quick visibility, it comes with limitations. Most notably, system calls aren’t always guaranteed to be invoked. io_uring, which can bypass them entirely, is a positive and great example. This highlights the trade-offs and design complexity involved in building robust eBPF-based security agents.

The fact that security vendors took advantage of a technology that was built for tracing and observability and used it for security is not inherently flawed. However,  it comes at a cost that needs to be addressed. This blog (eBPF Observability Tools Are Not Security Tools) by Brendan Gregg highlights just that.

Writing some io_uring based code

For the technical readers who want to get their hands dirty with io_uring find this gist as a short example of how you can read a file with io_uring.

To compile binaries with io_uring you can use the following commands.

git clone https://github.com/axboe/liburing.git
make -C ./liburing
gcc main.c -o ./program -I./liburing/src/include/ -L./liburing/src/ -Wall -O2 -D_GNU_SOURCE -luring
./program

Detection options

There are ways the io_uring rootkit can be detected. Security vendors should study them and improve their agent’s resilience to future bypasses.

Before we dive into the Linux ecosystem, let’s take a look at Windows because it highlights processes that need to be adopted in Linux. Back in the day, all EDRs and Anti-Virus software were hooking the SSDT to gain visibility into system calls. While this method worked, it was messy and caused a lot of problems and bypasses. Microsoft understood the need to create a structured mechanism for security vendors to hook, so they created a few. The most notable one is notification routines, it allows vendors to register callbacks on specific events but with the guarantee that it will get called, there isn’t a flow in the OS that won’t trigger these callbacks (on the spoken actions).

In the case of Linux, and specifically with eBPF, there is a relatively new mechanism called KRSI (Kernel Runtime Security Instrumentation). KRSI is built on top of the LSM (Linux Security Module) framework and allows security vendors to attach eBPF programs to LSM hooks. This is a significant step forward because, unlike direct syscall hooking, LSM hooks provide more consistent and reliable coverage for security-relevant actions, such as file operations, process execution, and network access.

Using KRSI, vendors can monitor high-level security events in a structured way, similar to Windows notification routines. The key advantage is that these LSM hooks are part of the kernel’s internal enforcement logic, meaning they are much harder to bypass through creative syscall avoidance techniques, like those employed by io_uring.

That said, the adoption of KRSI is still in its early stages. Not all distributions have it enabled by default, and vendors need to carefully design their monitoring strategies to avoid performance penalties or verifier rejections. However, its presence marks a clear shift toward more reliable detection strategies.

How can we detect an io_uring-based rootkit today?

Here are a few options:

Detecting anomalous usage of io_uring
Since the majority of applications don’t typically rely on io_uring, security vendors can monitor for unexpected usage patterns involving this interface. For instance, if an application that has historically never used io_uring suddenly begins leveraging it, that’s a clear red flag. This approach can be highly effective as an initial signal, but it comes with a caveat: if not implemented carefully, it can generate a high volume of false positives, especially in environments where new or updated software is regularly introduced.

Hooking up with KRSI
As mentioned earlier, this is likely the most robust long-term approach. KRSI (Kernel Runtime Security Instrumentation) offers native integration with the Linux security layer, enabling deep visibility into kernel-level events. It’s an evolving and promising technology that aligns well with the need for modern, flexible detection strategies—especially as attackers begin using more obscure kernel features.

Finding other hook points across the stack
Another viable approach involves identifying alternative hook points in different layers of the Linux stack. For example, you could locate a Kprobe that reliably executes each time a file is accessed. This strategy demands a deeper understanding of kernel internals and may require targeted research for each specific action you want to monitor. Additionally, it’s important to ensure that the hook point remains stable across kernel versions, as certain probes may be deprecated or removed. That said, there are still several well-established points in the kernel that offer consistency and can serve as solid monitoring anchors.

Final words

In this post, we presented our research on how to effectively bypass Linux runtime detection tools. This research is relevant to any Linux environment especially in the modern cloud-native environments. The io_uring mechanism, present in Linux since version 5.1, creates a dangerous blind spot that most security tools don’t look at.

Our team at ARMO developed “Curing”, a rootkit that operates entirely via io_uring, allowing it to completely bypass traditional syscall monitoring. This isn’t just a theoretical concern – we tested major security solutions including Falco, Tetragon, and Microsoft Defender, confirming they all miss these attacks. With Linux being the foundation of cloud infrastructure everywhere, this research impacts organizations across the board.

Fortunately, there’s hope. We outline several detection strategies for vendors, particularly advocating for KRSI implementation. Meanwhile, ARMO’s solutions have already addressed this vulnerability, providing the comprehensive protection that cloud-native environments desperately need in the face of this previously overlooked threat.

References

Close

Join the First Cloud Runtime Security Summit

Save your Spot city
slack_logos Continue to Slack

Get the information you need directly from our experts!

new-messageContinue as a guest