Two new page-cache-write LPE modules, both ported from the public V12 security PoCs (github.com/v12-security/pocs): - dirtydecrypt (CVE-2026-31635): rxgk missing-COW in-place decrypt. rxgk_decrypt_skb() decrypts spliced page-cache pages before the HMAC check, corrupting the page cache of a read-only file. Sibling of Copy Fail / Dirty Frag in the rxrpc subsystem. - fragnesia (CVE-2026-46300): XFRM ESP-in-TCP skb_try_coalesce() loses the SHARED_FRAG marker, so the ESP-in-TCP receive path decrypts page-cache pages in place. A latent bug exposed by the Dirty Frag fix (f4c50a4034e6). Retires the old _stubs/fragnesia_TBD stub. Both wrap the PoC exploit primitive in the skeletonkey_module interface: detect/exploit/cleanup, an --active /tmp sentinel probe, --no-shell support, and embedded auditd + sigma rules. The exploit body runs in a forked child so the PoC's exit()/die() paths cannot tear down the dispatcher. The fragnesia port drops the upstream PoC's ANSI TUI (incompatible with a shared dispatcher); the exploit mechanism is reproduced faithfully. Linux-only code is guarded with #ifdef __linux__ so the modules still compile on non-Linux dev boxes. VERIFICATION: ported, NOT yet validated end-to-end on a vulnerable-kernel VM. The CVE fix commits are not pinned, so detect() is precondition-only (PRECOND_FAIL / TEST_ERROR, never a blind VULNERABLE) and --auto will not fire them unless --active confirms. macOS stub-path compiles verified locally; the Linux exploit-path build is covered by CI (build.yml, ubuntu) only. See each MODULE.md. Wiring: core/registry.h, skeletonkey.c, Makefile, CVES.md, ROADMAP.md.
4.0 KiB
fragnesia — CVE-2026-46300
🟡 PRIMITIVE / ported. Faithful port of the public V12 PoC into the
skeletonkey_moduleinterface. Not yet validated end-to-end on a vulnerable-kernel VM — see Verification status below.
Summary
Fragnesia ("Fragment Amnesia") is an XFRM ESP-in-TCP local privilege
escalation. skb_try_coalesce() fails to propagate the
SKBFL_SHARED_FRAG marker when moving paged fragments between socket
buffers — so the kernel forgets that a fragment is externally backed by
page-cache pages spliced in from a file. The ESP-in-TCP receive path
then decrypts in place, corrupting the page cache of a read-only file.
Fragnesia is a latent bug exposed by the Dirty Frag fix: the
candidate patch cites the Dirty Frag remediation (f4c50a4034e6) as a
commit it "fixes". It is the same page-cache-write bug class as Copy
Fail / Dirty Frag, reached through a different code path.
Primitive
- Build a 256-entry AES-GCM keystream-byte table via
AF_ALGecb(aes)— for any wanted output byte, this yields the ESP IV whose keystream byte XORs the current byte to the target. - Enter a mapped user namespace + network namespace, bring
loopback up, and install an XFRM ESP-in-TCP state
(
rfc4106(gcm(aes)),TCP_ENCAP_ESPINTCP). - A receiver accepts a loopback TCP connection and flips it to the
espintcpULP; a sendersplice()s page-cache pages of the target file into that TCP stream behind a crafted ESP prefix. - The coalesce bug makes the kernel decrypt the spliced page-cache pages in place — one chosen byte per trigger.
The exploit rewrites the first 192 bytes of a setuid-root binary
(/usr/bin/su and friends) with an ET_DYN ELF that drops privileges to
0 and execves /bin/sh.
Operations
| Op | Behaviour |
|---|---|
--scan |
Checks unprivileged-userns availability + a readable setuid carrier ≥ 4096 bytes. With --active, runs the full ESP-in-TCP primitive against a disposable /tmp file and reports VULNERABLE/OK empirically. |
--exploit … --i-know |
Forks a child that places the payload into the carrier's page cache and execs it for a root shell. --no-shell stops after the page-cache write. |
--cleanup |
Evicts the carrier from the page cache. The on-disk binary is never written. |
--detect-rules |
Emits embedded auditd + sigma rules. |
Preconditions
- Unprivileged user namespaces enabled. On Ubuntu, AppArmor blocks
this by default —
sysctl kernel.apparmor_restrict_unprivileged_userns=0(or chain a separate bypass). This is the scoping question the old_stubs/fragnesia_TBDraised; the module ships and reportsPRECOND_FAILcleanly when the userns gate is closed. CONFIG_INET_ESPINTCPbuilt into the kernel.- A readable setuid-root binary ≥ 4096 bytes as the payload carrier.
- x86_64 (the embedded ELF payload is x86_64 shellcode).
Port notes
The upstream PoC renders a full-screen ANSI "smash frame" TUI
(draw_smash_frame + terminal scroll-region escapes). That is not
ported — it cannot coexist with a shared multi-module dispatcher.
Progress is logged with [*]/[+]/[-] prefixes, gated on --json.
The exploit mechanism itself is reproduced faithfully.
Verification status
This module is a faithful port of https://github.com/v12-security/pocs/tree/main/fragnesia, compiled into the SKELETONKEY module interface. It has not been validated end-to-end against a known-vulnerable kernel inside the SKELETONKEY CI matrix.
detect() deliberately does not return a kernel-version-based
patched/vulnerable verdict: the CVE-2026-46300 fix commit is not yet
pinned here. Instead:
- preconditions missing →
PRECOND_FAIL - preconditions present, no
--active→TEST_ERRORso--autodoes not fire it blind --active→ empirical VULNERABLE / OK via the/tmpsentinel probe
Before promoting to 🟢: pin the fix commit + branch-backport
thresholds, add a kernel_range, and validate on a vulnerable VM.