Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
copy-fail-CVE-2026-31431 — Copy Fail: 732 Bytes to Root on Every Major Linux Distribution. | Kitploit
Tools/GitHubGitHub/rio128128/copy-fail-cve-2026-31431
Privilege EscalationContainer SecurityExploit FrameworksVulnerability AnalysisExploitationPenetration TestingCloud SecurityRed TeamingBinary Exploitation
GitHubrio128128/copy-fail-cve-2026-31431

copy-fail-CVE-2026-31431

Copy Fail: 732 Bytes to Root on Every Major Linux Distribution.

255 months agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share
View Repository

CVE-2026-31431 — Copy Fail

732 bytes. Any distro. Root.

A straight-line logic flaw in the Linux kernel's authencesn cryptographic template enables an unprivileged local user to perform a precise, controlled 4-byte write into the page cache of any readable file — including setuid binaries. No races. No retries. No recompilation. Root on every major Linux distribution shipped since 2017.

📄 Technical Write-up  ·  🔗 Kernel Patch  ·  🛡️ CVSS: Critical


Tested Distributions

DistroKernel Version
Ubuntu 24.04 LTS6.17.0-1007-aws
Amazon Linux 20236.18.8-9.213.amzn2023
RHEL 10.16.12.0-124.45.1.el10_1
SUSE 166.12.0-160000.9-default

All four were rooted using the identical 732-byte Python script, without modification.


What Makes This Different

PropertyDetail
DeterministicStraight-line logic flaw — no race conditions, no timing windows, no retries
PortableSame script, same bytes, works across all tested distros and architectures
Tiny732-byte Python script using only the standard library (os, socket, zlib). Requires Python 3.10+ for os.splice
StealthyThe corrupted page is never marked dirty. On-disk checksums are unchanged; only the in-memory page cache is modified
Cross-containerThe page cache is shared system-wide across container boundaries — this is also a Kubernetes node escape primitive (see Part 2)

Root Cause

The Setup: Page Cache Pages in a Writable Scatterlist

AF_ALG exposes the kernel's crypto subsystem to unprivileged userspace. splice() transfers file data into a pipe by reference — passing page cache pages directly, without copying. When a user splices a file into an AF_ALG AEAD socket, the socket's input scatterlist holds live references to the kernel's cached pages of that file.

In algif_aead.c, the 2017 in-place optimization copied AAD and ciphertext from the TX scatterlist into the RX buffer, but chained the authentication tag pages by reference using sg_chain(), then set req->src = req->dst:

Input SGL:   [ AAD | CT | Tag ]
                              ^
                              └─ sg_chain() → still points to page cache pages

Output SGL:  [ AAD | CT ] ──→ [ Tag (page cache pages) ]
              (RX buffer)       (chained from TX SGL)

req->src ──┐
           ├──→ same combined scatterlist
req->dst ──┘

Page cache pages from splice() were now sitting inside a writable destination scatterlist, separated from the legitimate write region by only an offset boundary. Nothing in the API enforced that algorithms must stay within bounds.

The Trigger: authencesn's Out-of-Bounds Scratch Write

authencesn is an AEAD wrapper used by IPsec for 64-bit Extended Sequence Number (ESN) support. To rearrange ESN bytes for HMAC computation, it uses the caller's destination buffer as scratch space — including a write at offset assoclen + cryptlen, which lies past the authentication tag boundary:

scatterwalk_map_and_copy(tmp,     dst, 0,                       8, 0); // read AAD[0..7]
scatterwalk_map_and_copy(tmp,     dst, 4,                       4, 1); // overwrite dst[4..7]
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen,     4, 1); // ← writes past the tag

The third call writes 4 bytes (seqno_lo) at dst[assoclen + cryptlen]. In the in-place AF_ALG path, the scatterwalk crosses from the RX buffer into the chained page cache tag pages. The kernel maps the page cache page via kmap_local_page and writes directly into the cached copy of the target file.

The HMAC then fails (the ciphertext is fabricated), recvmsg() returns an error — but the 4-byte write persists permanently.

The Three Attacker-Controlled Variables

VariableControlled Via
Target fileAny file readable by the current user
Write offsetassoclen, splice offset, and splice length
Write valueBytes 4–7 of the AAD supplied in sendmsg() (seqno_lo)

How It Happened: A Nine-Year Chain

YearEvent
2011authencesn added to the kernel (a5079d084f8b) for IPsec ESN support. The scratch write existed but was harmless — only the internal xfrm layer called it, and AAD lived in a separate scatterlist.
2015AF_ALG gains AEAD support. authencesn converted to the new AEAD interface (104880a6b470), introducing the assoclen + cryptlen write offset. Still out-of-place: page cache pages were in src (read-only). Not yet exploitable.
2017In-place optimization added to algif_aead.c (72548b093ee3). req->src = req->dst. Page cache tag pages chained into the writable destination. Vulnerability formed.
2026-03-23Reported to the Linux kernel security team.
2026-04-01Patch merged into mainline.
2026-04-22CVE-2026-31431 assigned.
2026-04-29Public disclosure.

No single change was individually wrong. The vulnerability lives at the intersection of all three.


Exploit

The default target is /usr/bin/su, a setuid-root binary present on all tested distributions.

Step 1 — Socket setup
  Open AF_ALG socket, bind to authencesn(hmac(sha256),cbc(aes))
  Set key. Accept request socket. (No privileges required.)

Step 2 — Write loop (once per 4-byte shellcode chunk)
  sendmsg()  →  AAD bytes [4:8] carry the 4 bytes to write (seqno_lo)
  splice()   →  target file's page cache pages into the AF_ALG socket
  recv()     →  triggers decrypt → authencesn writes seqno_lo into page cache
               (recvmsg returns error; the write persists)

Step 3 — Execute
  execve("/usr/bin/su")
  Kernel loads binary from the (now-corrupted) page cache
  Setuid-root binary executes injected shellcode → UID 0
a = socket.socket(38, 5, 0)                          # AF_ALG, SOCK_SEQPACKET
a.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
# ... set key, accept request socket u ...
u.sendmsg([b"A"*4 + payload_chunk], [cmsg_headers], MSG_MORE)
os.splice(target_fd, pipe_wr, offset)
os.splice(pipe_rd, alg_fd, offset)
u.recv(...)                                          # triggers page cache write

Remediation

Permanent Fix

Update to a kernel containing patch a664bf3d603d. The fix reverts algif_aead.c to out-of-place operation: req->src points to the TX SGL; req->dst points to the RX buffer. Page cache pages from splice() remain read-only. The sg_chain() mechanism that linked them into the writable destination is removed.

// Before (vulnerable): src and dst share the same scatterlist
aead_request_set_crypt(&areq->cra_u.aead_req, rsgl_src, rsgl_src, used, ctx->iv);

// After (fixed): src is TX SGL, dst is RX buffer — fully separated
aead_request_set_crypt(&areq->cra_u.aead_req, tsgl_src, rsgl_dst, used, ctx->iv);

Immediate Mitigation

Disable the algif_aead kernel module:

echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null

Or block AF_ALG socket creation via a seccomp policy in your workload profiles.

Note for container environments: Because the page cache is shared across the host, this vulnerability crosses container boundaries. Apply mitigations at the node level, not just per-pod. See Part 2 for full Kubernetes escape details.


Coordinated Disclosure Timeline

Download Tool