The RxRPC Page-Cache Write vulnerability is a fully unprivileged kernel LPE in the RxRPC/rxkad packet verification path. When a UDP-framed RxRPC DATA packet arrives and its payload was injected viaDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/V4bel/dirtyfrag/llms.txt
Use this file to discover all available pages before exploring further.
splice from a read-only page cache page, rxkad_verify_packet_1() runs an in-place pcbc(fcrypt) single-block decrypt directly on that page cache page. The 8-byte STORE value is fcrypt_decrypt(C, K) where C is the existing ciphertext in the file and K is an 8-byte session key you register via add_key("rxrpc", ...) without any privilege. By brute-forcing K in user space until the decrypt produces the desired plaintext, you can rewrite 8 bytes of any readable file — in practice, the root entry of /etc/passwd — and obtain a root shell via PAM nullok.
Root cause
rxkad_verify_packet_1() is responsible for authenticating the first 8 bytes of an incoming RxRPC payload at security level RXRPC_SECURITY_AUTH. It does so by performing an in-place single-block pcbc(fcrypt) decrypt:
net/rxrpc/rxkad.c
[4], src and dst are the same scatter-gather list (sg, sg), making the operation in-place. skb_to_sgvec() converts the skb’s frag directly into that SGL, so a page cache page P that you spliced into the frag becomes both the source and the destination of the decrypt. At [5], an 8-byte STORE happens directly on top of page P.
Because the IV is all-zero and the operation covers exactly one 8-byte block, pcbc_decrypt(C, K, IV=0) reduces to a plain fcrypt_decrypt(C, K). The value STOREd into the page cache is therefore fcrypt_decrypt(C, K), where C is the 8 bytes that already exist at the splice offset in the file and K is the session key you supply.
Where K comes from
The cipher transform incall->conn->rxkad.cipher is initialized from the session_key field of an RxRPC v1 token. You register that token with:
add_key("rxrpc", ...) requires no privilege. You can call it as an ordinary user and register any 8-byte K you choose.
Key differences from the ESP variant
No namespace required
Unlike the ESP variant, you do not call
unshare() or need CAP_NET_ADMIN. All primitives — add_key, socket(AF_RXRPC), socket(AF_ALG), splice, recvmsg — are available to unprivileged users.8 bytes per STORE
Each trigger writes 8 bytes rather than 4. The STORE value is
fcrypt_decrypt(C, K), not a directly controlled value, so you must brute-force K in user space before each trigger.Requires rxrpc.ko
The module is loaded by default on Ubuntu (triggered by
socket(AF_RXRPC, ...)), but is absent from RHEL/CentOS default builds. This is the RxRPC variant’s blind spot.Target: /etc/passwd
Because the STORE value is cipher-constrained, you cannot write an arbitrary ELF. Instead you overwrite the password field of the root entry in
/etc/passwd to empty, exploiting PAM nullok.Exploit overview
The exploit targets line 1 of/etc/passwd. The normal line looks like:
passwd field becomes an empty string. pam_unix.so with nullok accepts an empty password and returns PAM_SUCCESS without a prompt. su then calls setresuid(0,0,0) and drops into a root bash shell.
The three-splice chain
A single 8-byte STORE cannot reshape all of characters 4–15 in one shot. The exploit uses last-write-wins across three overlapping positions:K_C feasible.
Chained-ciphertext correction
After splice A is applied to the page cache, the ciphertext that splice B sees at offsets 6–11 is no longer the original file content — it isP_A[2..7] (the bytes that splice A STOREd). You must account for this chain when brute-forcing K_B and K_C:
fcrypt has a 56-bit key and the user-space port runs at approximately 18 million decrypts per second. With only 2 bytes tightly constrained per splice (characters 4–5, 6–7, 8–9) and 5 loosely constrained bytes for splice C, the effective search space is small enough that K_A and K_B are found in roughly 5 ms each, and K_C in roughly 1 second.
Key registration
For each of the three positions, you register a fresh key with a unique description:sec_ix = 2 (RXKAD) and the 8-byte K placed in the session_key field. No privilege is required.
The first
socket(AF_RXRPC, ...) call autoloads rxrpc.ko via MODULE_ALIAS_NETPROTO(PF_RXRPC). No explicit modprobe is needed on Ubuntu.Trigger mechanism
Each of the three STORE operations follows the same handshake flow:Client initiates call
Create an
AF_RXRPC client socket bound to port C, set RXRPC_SECURITY_KEY to the key description, and set RXRPC_MIN_SECURITY_LEVEL to RXRPC_SECURITY_AUTH (1). Call sendmsg to initiate an RPC call toward a fake UDP server on port S.Fake server sends CHALLENGE
A plain UDP socket on port
S receives the client’s DATA/PING packet, extracts (epoch, cid, callNumber), and sends back a forged CHALLENGE:Client sends RESPONSE, connection security is initialized
On receiving the CHALLENGE, the client automatically generates and sends a RESPONSE using
K, and initializes conn->rxkad.cipher with pcbc(fcrypt) keyed by K. The fake server drains the RESPONSE and ignores it (no valid ticket is needed).Precompute wire cksum
Compute
csum_iv by PCBC-encrypting {epoch, cid, 0, sec_ix=2} with IV=K, then compute the wire cksum from {call_id, (cid_low2 << 30) | seq} with IV=csum_iv. Both operations use socket(AF_ALG) with pcbc(fcrypt):Splice page cache page into DATA packet
Build a pipe. Use
vmsplice to insert the 28-byte forged DATA wire header into the pipe, then splice 8 bytes of /etc/passwd at splice_off into the next pipe slot, then splice the entire pipe into the UDP socket connected to the client:MSG_SPLICE_PAGES is set automatically, planting the /etc/passwd page cache page directly into the frag of the sender skb.Call chain
Patch
The existing gate before in-place decrypt checked onlyskb_cloned(skb). Adding || skb->data_len to the condition ensures that non-linear skbs — including those with splice-planted frags — are isolated via skb_copy() before any in-place operation:
As of disclosure (2026-05-08), this patch has not been merged upstream. The patch was submitted to the netdev mailing list at
https://lore.kernel.org/all/afKV2zGR6rrelPC7@v4bel/.Disclosure timeline
2026-04-29 — Initial disclosure and patch
2026-04-29 — Initial disclosure and patch
Detailed information about the RxRPC vulnerability and a weaponized exploit were submitted to
security@kernel.org. A patch was submitted to the netdev mailing list and the issue was made public.2026-05-07 — linux-distros embargo and early break
2026-05-07 — linux-distros embargo and early break
Detailed information and the exploit were submitted to the linux-distros mailing list with a 5-day embargo. An unrelated third party published the ESP exploit publicly the same day, triggering early full disclosure of the entire Dirty Frag document after agreement with distribution maintainers.
2026-05-08 — CVE assignment
2026-05-08 — CVE assignment
CVE-2026-43500 was reserved for tracking this vulnerability.