Off By !: Exploiting a Use-after-Free in the Linux Kernel - Exodus Intelligence
Skip to content
EXODUS BLOG
Off By !: Exploiting a Use-after-Free in the Linux Kernel
JUNE 8, 2026<br>Vulnerability Analysis, Exploit Techniques, General Research
By Oliver Sieber
Overview
In this blog post, we discuss a use-after-free vulnerability that we found in the nftables subsystem of the Linux kernel in early 2025. This vulnerability was patched upstream on 5 February 2026 and assigned CVE-2026-23111.
This blog post covers a technical analysis of the vulnerability and how we exploited it to perform a local privilege escalation from an unprivileged user to root on Debian Bookworm, Debian Trixie, Ubuntu 22.04 LTS, and Ubuntu 24.04 LTS.
Preliminaries
This section is dedicated to the introduction of the main structures of nftables and the concept of generation masks. If you are familiar with this subsystem, feel free to skip this section.
Another recommended introduction to nftables is given by the blog post How The Tables Have Turned: An analysis of two new Linux vulnerabilities in nf_tables.
The main structures are nft_table, nft_chain, nft_rule, nft_expr, nft_set, and nft_set_elem. A simplification of their dependencies is illustrated in the image below.
Netfilter
Netfilter is the Linux kernel packet filtering framework, and it is commonly associated with iptables and its successor nftables. It enables packet filtering, network address and port translation, packet logging, userspace packet queuing, and other packet mangling.
The netfilter hooks are a framework inside the Linux kernel that allows kernel modules to register callback functions at different locations of the Linux network stack. The registered callback function is then called for every packet that traverses the respective hook within the Linux network stack.
The iptables and now nftables frameworks allow defining rule sets and work by interacting with the packet filtering hooks defined by the netfilter framework.
nftables
In nftables, the top-level containers within a given rule set are the tables (struct nft_table). They can hold chains, sets, maps, flowtables, and stateful objects. Each table belongs to exactly one family, where each family corresponds to a different networking level (e.g., ip for IPv4, ip6 for IPv6, arp for ARP, etc.).
Chains
Chains (struct nft_chain) are the next topmost level containers. They are associated with tables and can have rules associated to them. Chains allow processing packets at a particular processing step. To do so, a base chain of the desired type (i.e., filter, route, or NAT) has to be created and then attached to the appropriate netfilter hook (e.g. ingress, pre-routing, input, forward, output, and post-routing).
Rules
Rules (struct nft_rule) are the elements that specify which action to take on network packets based on whether they match the specified criteria. Each rule consists of zero or more expressions followed by one or more statements. Each expression tests whether a packet matches a specific payload field or packet/flow metadata. Multiple expressions are linearly evaluated from left to right; if the first expression matches, then the next expression is evaluated, and so on. If all the expressions in a rule are matched by a given packet, the rule’s statements are executed. A statement defines which action to take, such as counting, logging, accepting or dropping the packet.
The rules of a chain are connected to each other via a doubly linked list.
Verdicts
Verdicts (struct nft_verdict) in nftables are the outcomes of evaluating packet rules within a chain. When a packet matches a rule, a verdict determines the subsequent action: continued evaluation within the current chain, redirection to another chain, or termination of processing with acceptance or rejection of the packet.
A few of the common verdicts are:
NFT_CONTINUE: continue evaluation of the current rule.
NFT_BREAK: terminate evaluation of the current rule.
NFT_JUMP: push the current chain on the jump stack and jump to a chain.
NFT_GOTO: jump to a chain without pushing the current chain on the jump stack.
NFT_RETURN: return to the topmost chain on the jump stack.
NF_DROP: drop the packet. No further evaluation takes place.
NF_ACCEPT: accept the packet.
Expressions
Expressions (struct nft_expr) are sequences of operations that are evaluated one after another to form a rule. They are used to represent either data gathered from the packet during rule set evaluation or constant values like network addresses and port numbers. Expressions can be merged using binary, logical, relational, and other types of expressions to form complex or compound expressions. They are also used as arguments for certain types of<br>operations like NAT and packet marking.
Examples of such expressions are:
nft_immediate: loads an immediate value into a register.
nft_cmp: compares given data with data from a given register.
nft_meta: set/get packet...