Tech 13 min read

Linux DirtyDecrypt (CVE-2026-31635): RxGK page cache LPE on CONFIG_RXGK kernels

IkesanContents

TL;DR

What broke Linux kernel RxRPC / RxGK path. On kernels with CONFIG_RXGK enabled, a public PoC escalates a regular user to root

Who’s affected Distros that track upstream kernels closely — Fedora, Arch Linux, openSUSE Tumbleweed. Debian stable and Ubuntu LTS stock kernels are not in the same bucket

What to check The kernel.org-sourced NVD entry for CVE-2026-31635 reads like a DoS, but the same record now references the DirtyDecrypt PoC. Look at CONFIG_RXGK, the fix commit, and your distro’s state — not just the CVE number


DirtyDecrypt (also called DirtyCBC) is a Linux kernel local privilege escalation through the RxRPC RxGK security class, with a public PoC tied to CVE-2026-31635.
V12 Security published the PoC on May 19, 2026, and The Hacker News covered it the same day.

Like Copy Fail, Dirty Frag, and Fragnesia before it, DirtyDecrypt doesn’t write the on-disk file. It corrupts the page cache copy Linux holds in RAM.
What’s different is the entry point: DirtyDecrypt goes through the RxGK security class of RxRPC.

RxRPC is the RPC protocol used by AFS and friends, and RxGK is its GSSAPI-based security class.
The kernelconfig.io entry for CONFIG_RXGK says it provides the RxGK security class for AFS and pulls in crypto dependencies as a bundle.
Rather than framing this as a “generic Linux web server / Ubuntu LTS stock kernel” issue, it’s better read as something that hits distros tracking upstream closely — developer workstations, Kubernetes workers, shared CI runners.

What RxRPC and RxGK actually are

RxRPC is the RPC protocol used by CMU’s Andrew File System (AFS) and its descendants OpenAFS and AuriStorFS.
Inside the Linux kernel it lives under net/rxrpc/ and is exposed to userspace through the AF_RXRPC socket family.
It doesn’t replace TCP or UDP — it’s an AFS-specific RPC layer that runs on top of UDP and adds multiplexing, retransmission, and ordering.

Security classes are how RxRPC traffic is protected.
rxkad, the older one, is Kerberos 4-based — the Dirty Frag article touched on it as an entry point.
rxgk (Rx GSSAPI Kerberos) is newer, assumes Kerberos 5 / GSSAPI, and supports AES-GCM, AES-CTS, and HMAC-SHA-family algorithms with integrity checks.
Modern AFS stacks are migrating from rxkad to rxgk, and CONFIG_RXGK is the in-kernel landing for that.

Security classKey infrastructureCryptoKernel configRelated bug
rxnullnoneplaintextCONFIG_RXRPC only
rxkadKerberos 4fcrypt / DESCONFIG_RXKADDirty Frag (RxRPC side)
rxgkKerberos 5 / GSSAPIAES-GCM / AES-CTS+HMACCONFIG_RXGKDirtyDecrypt

Per kernelconfig.io, enabling CONFIG_RXGK pulls in rxrpc itself together with AES-GCM, AES-CTS, and HMAC-SHA-family crypto dependencies.
Even if you don’t use AFS, if your distro ships CONFIG_AFS_FS=m and CONFIG_RXGK=y, the relevant code is reachable through the rxrpc.ko load path.
DirtyDecrypt’s entry point is the RxGK response verification path — a different function and different logic from the rxkad_verify_packet_1() covered in the Dirty Frag piece.
The skeleton is the same as the rest of the series: page-cache-backed pages enter skb frags, and crypto processing writes to them in place.

The CVE write-up and the PoC don’t quite match

The kernel.org-sourced description on the NVD CVE-2026-31635 page says the length check in rxgk_verify_response() is inverted — an oversized RESPONSE authenticator reaches rxgk_decrypt_skb() and hits a BUG_ON(len).
The CNA-side CVSS 3.1 score is 7.5 HIGH, with a vector that reads as remotely-reachable availability impact.

V12’s PoC README tells a different story: rxgk_decrypt_skb lacks copy-on-write protection and that causes an rxgk pagecache write.
The Hacker News treats the PoC as CVE-2026-31635 because the NVD record now points at it.
NVD’s own change log shows CISA-ADP added the V12 DirtyDecrypt reference on May 18, 2026.

The practical difference for operators: read the record as “CVSS 7.5, availability only” or as “DirtyDecrypt PoC was attached to this record, so treat it as LPE.”
If you go with the latter, score matters less — kernel configuration and patched packages are what you check first.

RxGK in-place decryption touches the page cache

The center of DirtyDecrypt is rxgk_decrypt_skb().
sk_buff is the Linux network stack’s packet representation; alongside its linear buffer, it can hold page-sized fragments.
As we’ve seen since Copy Fail, when splice() or zero-copy paths pass file-backed pages into kernel processing, the receiver is supposed to treat them as read-only shared pages.

The protection is copy-on-write.
If you make a private copy before writing to a shared page, the crypto code’s scratch writes can’t leak into another process’s or another file’s page cache.
DirtyDecrypt’s RxGK decryption path skips that step and writes in place into page-cache-backed pages.

flowchart TD
    A["Readable file"] --> B["Page cache page"]
    B --> C["Reference enters skb frag"]
    C --> D["RxGK decryption path"]
    D --> E["No copy-on-write,<br/>in-place write"]
    E --> F["In-memory copy of setuid<br/>binary is corrupted"]

From the PoC side, the attack chain stays inside local-user privileges.
No user namespace, no CAP_NET_ADMIN — just creating an AF_RXRPC socket and registering an RxGK token via add_key("rxrpc", ...) is enough to reach the entry point.

sequenceDiagram
    participant U as Unprivileged user
    participant K as Kernel
    participant PC as Page cache
    participant RX as RxRPC/RxGK path
    U->>K: open + read /usr/bin/su
    K->>PC: Target binary loaded into page cache
    U->>K: Create AF_RXRPC socket + add_key("rxrpc")
    U->>K: splice/MSG_SPLICE_PAGES references pages in send skb
    K->>RX: Page-cache-backed page in skb frag
    U->>RX: Send crafted RESPONSE packet
    RX->>PC: rxgk_decrypt_skb writes in place
    U->>K: execve su
    K-->>U: Runs with modified cache → root shell

Fragnesia came in through ESP-in-TCP, Dirty Frag through ESP and RxRPC (rxkad), Copy Fail through AF_ALG.
DirtyDecrypt is a separate path leaning on RxGK. Patching up through Fragnesia leaves this one open.
The affected surface didn’t grow — the entry condition just shifted again.

How it lines up against the previous three

This series keeps digging into the same bug class through different entry points.
DirtyDecrypt is easier to prioritize when you put it next to the previous three.

NameCVEEntry pointRequired kernel configRequired privilegeWorkaround module block
Copy FailCVE-2026-31431splice()AF_ALG/algif_aeadCONFIG_CRYPTO_USER_API_AEADregular user onlyalgif_aead
Dirty Frag (ESP)CVE-2026-43284UDP skb frag → esp_input()CONFIG_INET_ESP + usernsCAP_NET_ADMIN (obtainable via unshare)esp4 / esp6
Dirty Frag (RxRPC)CVE-2026-43500rxkad_verify_packet_1()CONFIG_RXKADregular user (via add_key)rxrpc
FragnesiaCVE-2026-46300skb_try_coalesce() → ESP-in-TCPCONFIG_XFRM_ESPINTCPusernsesp4 / esp6 / espintcp
DirtyDecryptCVE-2026-31635rxgk_decrypt_skb()CONFIG_RXGKregular userrxrpc

What changed with DirtyDecrypt is that the rxkad path of Dirty Frag depended on CONFIG_RXKAD, but this one moved to CONFIG_RXGK.
If you’ve already blocked rxrpc loading as a Dirty Frag workaround, the module can’t come up, so the RxGK path is also closed.
On the other hand, if you only cared about the rxkad path and patched that specific function in Dirty Frag, DirtyDecrypt sits in a different function and survives.
As bug classes, they split per entry point: SKBFL_SHARED_FRAG-related ESP paths (Fragnesia), the rxkad path that doesn’t look at SKBFL_SHARED_FRAG (Dirty Frag), and the RxGK path where skb_cow_data()-equivalent COW is missing (DirtyDecrypt).

The affected set narrows down quickly by kernel config

The Hacker News writes that DirtyDecrypt’s impact is limited to distros where CONFIG_RXGK is enabled.
The examples it lists are Fedora, Arch Linux, and openSUSE Tumbleweed.
Moselwal’s investigation also categorizes Debian Stable, RHEL, and Ubuntu LTS stock kernels as either RxGK-disabled or missing the relevant code, while highlighting Fedora / Arch / Tumbleweed, mainline PPA, and kernel-ml-style upstream-leaning kernels.

On the Ubuntu CVE page, as of May 20, 2026, the Ubuntu 25.10 series — linux, linux-aws, linux-azure, linux-gcp, linux-oracle, linux-raspi and so on — is marked Vulnerable, while most 24.04 LTS and 22.04 LTS stock kernels are Not affected.
Debian security tracker marks bullseye, bookworm, and trixie as vulnerable code not present.
This isn’t “all of Linux” — it splits along which kernel series you’re running.

Verification starts around here.

uname -r
zcat /proc/config.gz 2>/dev/null | grep RXGK || grep RXGK /boot/config-$(uname -r) 2>/dev/null
lsmod | grep -E '^(rxrpc|afs)\b'
modprobe -n -v rxrpc

If you see CONFIG_RXGK=y or CONFIG_RXGK=m, follow the CVE-2026-31635 equivalent fixes through your distro’s CVE page and kernel update history.
If you see # CONFIG_RXGK is not set, DirtyDecrypt’s PoC condition doesn’t apply.
Even so, on the same host, keep your algif_aead and esp4/esp6 workarounds in place for unrelated reasons.

Why Fedora, Arch, and Tumbleweed have it on

The reason CONFIG_RXGK tends to be enabled on “distros tracking upstream closely” is fairly simple as a policy matter.
These distros enable upstream kernel feature options broadly by default and leave the “do you actually need it” question to the user.
Fedora’s kernel configuration policy in particular requires maintainer review before turning a default-on option off, so new options typically land as =m unless there’s a specific reason not to.

Debian stable and Ubuntu LTS, on the other hand, care about long-term support, distributed binary size, and attack surface, so they don’t enable new upstream feature options as-is.
RxGK is “a new security class for AFS, a filesystem that isn’t widespread in modern cloud environments,” so on LTS-style series it tends to stay is not set.
That’s why you see Not affected on Ubuntu 24.04 LTS and 22.04 LTS, and on Debian bookworm and trixie.

Most users don’t actually use AFS, yet rxrpc.ko is loadable from unprivileged context — that’s the real entry point for impact.
If modprobe -n -v rxrpc doesn’t return an error, there’s a path for an unprivileged user to load the module through some trigger.
The Linux kernel does provide kernel.modules_disabled sysctl and per-module modprobe.blacklist, but by default many protocol modules are reachable from unprivileged context.
Autoload on AF_RXRPC socket creation is the typical example.

Tracking the fix commit and distro patches

Because the NVD description and the DirtyDecrypt PoC description for CVE-2026-31635 don’t line up, you can’t narrow the fix to a single commit.
You need to watch at least two.

The first is the fix for the inverted length check in rxgk_verify_response(), which addresses the DoS framing in NVD.
The second is the change that makes rxgk_decrypt_skb() go through skb_cow_data()-equivalent COW instead of writing in place into page-cache-backed pages — that’s the LPE-side fix.
The relevant changes should land around net/rxrpc/rxgk_app.c and net/rxrpc/rxgk.c, and you can follow backports into the kernel.org stable trees from there.

git clone --depth=200 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
cd linux
git log --oneline -- net/rxrpc/rxgk_app.c net/rxrpc/rxgk.c
git log --grep='rxgk' --since=2026-04-01 --oneline

For distro tracking, check the vendor advisory in addition to the CVE number.
Vendors backport fixes without bumping the upstream version, so uname -r isn’t a safe verdict.
Keep these URL patterns handy:

  • Ubuntu: https://ubuntu.com/security/CVE-2026-31635
  • Debian: https://security-tracker.debian.org/tracker/CVE-2026-31635
  • Fedora: https://bodhi.fedoraproject.org/updates/?search=CVE-2026-31635
  • openSUSE / SUSE: https://www.suse.com/security/cve/CVE-2026-31635.html
  • Arch: https://security.archlinux.org/CVE-2026-31635
  • Red Hat: https://access.redhat.com/security/cve/CVE-2026-31635
  • AlmaLinux / Rocky: search by the upstream RHEL errata ID

When a vendor publishes “Not affected,” read the reasoning too.
Amazon Linux declared no impact on Fragnesia because it doesn’t ship espintcp; for DirtyDecrypt, the same pattern could show up keyed on CONFIG_RXGK is not set.

Don’t run the PoC in containers or CI to verify

In this family, the PoC itself corrupts the page cache.
As covered in the Fragnesia piece, running the PoC in production to check “are we vulnerable” can corrupt your own in-memory copies of setuid binaries and auth files.
DirtyDecrypt is handled the same way.

On Kubernetes workers and self-hosted CI runners, updating container images doesn’t fix this.
Containers share the host kernel, so what you check is the worker node’s kernel, CONFIG_RXGK, the load state of rxrpc / afs, and the node image update date.
For runners that execute untrusted jobs or PRs, local privilege escalation is the front half of host takeover.

A reasonable interim mitigation is to block rxrpc from loading.
If you’ve already blocked esp4, esp6, and rxrpc as part of Dirty Frag / Fragnesia mitigation, you don’t need a new config file for DirtyDecrypt — just verify that the existing setting reached every node.
If you actually use AFS or RxRPC, expect communication impact and check service by service before rolling this out.

# If you already have a Dirty Frag mitigation, just verify rxrpc is in it
grep -R 'install rxrpc' /etc/modprobe.d/ 2>/dev/null

# If nothing is in place yet
sudo sh -c "printf 'install rxrpc /bin/false\n' > /etc/modprobe.d/dirtydecrypt.conf"
sudo rmmod rxrpc 2>/dev/null || true
sudo update-initramfs -u -k all 2>/dev/null  # Debian/Ubuntu family
sudo dracut -f 2>/dev/null                   # Fedora/RHEL family

# Quick check whether AFS / RxRPC is actually in use (look before adding workarounds)
mount | grep -E '\bafs\b'
systemctl list-units 2>/dev/null | grep -Ei 'openafs|auristor|kafs'
ss -ulnp 2>/dev/null | grep -E '7000|7001'   # default AFS port range

When you push this to Kubernetes nodes, freeze /etc/modprobe.d/ through a DaemonSet or the node image build.
The cross-node verification with kubectl debug from the Fragnesia piece works for DirtyDecrypt as-is.

References