Copy Fail: CVE-2026-31431 (TryHackMe)
Exploit copy-fail, a kernel LPE that corrupts any file’s page cache to gain root in seconds.

Task 1: Introduction
CVE-2026-31431, nicknamed Copy Fail, is none of those PE exploits that depend on precise kernel version offsets, require winning a race condition reliably, or crash the system when timing slips.
The researcher’s own description of the primitive frames it concisely: “an unprivileged local user can write four controlled bytes into the page cache of any readable file on a Linux system, and use that to gain root”
Copy Fail was assigned CVE-2026-31431 and a CVSS v3.1 base score of 7.8(High).
If you are familiar with CVE-2022-0847(Dirty Pipe), the class of primitive that drives Copy Fail will already be familiar. Both exploits write into the in-memory page cache of a file without touching the on-disk content, and both bypass file integrity monitoring as a result.
Learning Objectives
- explain the page cache write primitive that makes Copy Fail exploitable and why it bypasses file integrity monitoring.
- Identify the 4 kernel components (page cache, AF_ALG, authencesn, splice) that combine to create the vulnerability
- Execute the proof-of-concept as an unprivileged user and obtain a root shell.
- describe the primary detection signals and apply the
modprobemitigation on Ubuntu and Debian systems
Task 2: The Vulnerability
The page cache is like a shared whiteboard in a classroom.
- when someone reads a file from a disk, Linux puts a copy in the page cache
- everyone else reads from that same copy, not from the disk again
- writes to the file normally update both disk and cache
but here’s the trick: you can change the cache without touching the disk. That means integrity tools that check the disk will see the original, clean file. But the kernel will run the corrupted cache version.
AF_ALG: a crypto socket any user can open
AF_ALG is a special type (family 38) that lets normal programs use the kernel’s crypto engine.
No special permission needed. It’s meant for IPsec, disk encryption, hashing, etc.
Why this matters: An attacker without root access can open an AF_ALG socket and ask the kernel to do crypto operations.
authencesn: a tiny, guaranteed write
authencesn is a crypto template for IPsec with extended sequence numbers(ESN)
During encyrption, it writes 4 bytes (part of the sequence number) into the output buffer. Where in the buffer? Attacker controls that via assoclen and cryptlen
Critical detail: The write happens before the HMAC tag is verified. If HMAC fails (and the attacker makes sure it fails), the error returns to the user, but the 4-byte write says. No undo, no rollback, on cleanup.
So, attacker can write any 4-byte value to any offset inside the crypto output buffer.
splice(): moving pages, not copying data
splice() is a fast way to move data between file and sockets without copying bytes. Instead, it passes references to memory pages.
If you splice from /usr/bin/su into an AF_ALG socket, the socket now holds references directly to the kernel’s own page cache pages for that binary.
Normal write to AF_ALG -> user memory but splice() into AF_ALG -> kernel file cache pages
Questions:
What year was the optimization introduced that created this vulnerability? Ans: 2017
Which AEAD algorithm template performs the scratch write that corrupts the page cache? Ans: authencesn
What system call transfers page cache pages into the AF_ALG socket without copying them? Ans: splice
The HMAC verification fails after the scratch write is performed. Does this undo the page cache corruption? (Answer Format: Yay or Nay) Ans: Nay
Task 3: Exploitation
Step 1: Confirm Your Context
Confirm the current user has no elevated privileges:
karen@ubuntu:~$ iduid=1001(karen) gid=1001(karen) groups=1001(karen)This is the starting context the exploit requires. No elevated privileges, no special groups, just an ordinary local user.
Step 2: Inspect the Proof of Concept
The exploit script lives at /home/karen/exploit.py. Before running it, take a quick look at its high-level structure:
head -30 /home/karen/exploit.pyThe script uses only Python standard library modules. The os module supplies splice() and execve(), socket provides the AF_ALG socket calls, and zlib is used for CRC calculations when building the authentication key blob. There are no pip packages and no compiled extensions. The script runs on any Python 3.10 or later installation. Ubuntu 24.04 ships Python 3.12, so os.splice is available out of the box.
At a high level, the script performs the following steps in sequence:
- Opens an
AF_ALGsocket bound toauthencesn(hmac(sha256),cbc(aes)) - Calculates the target offset within
/usr/bin/suwhere each shellcode chunk should land - Constructs the AAD so that bytes 4-7 carry the shellcode value to write as
seqno_lo - Calls
splice()to feed page cache pages from/usr/bin/suinto the socket at the calculated offset - Calls
recvmsg()to trigger the AEAD decryption, at which pointauthencesnperforms its scratch write and the shellcode bytes land in the page cache - Repeats approximately 40 times to write successive 4-byte shellcode chunks
- Calls
os.execve("/usr/bin/su", ...)so the kernel loads from the corrupted cache and the shellcode runs
Step 3: Run the Proof of Concept
Execute the script:
python3 /home/karen/exploit.pyThe script prints progress as it writes each 4-byte chunk. After the writes complete, it calls execve() on the target binary. The shell prompt should change and the effective user ID becomes root:
karen@ubuntu:~$ python3 /home/karen/exploit.py# whoamiuid=0(root) gid=0(root) groups=0(root)Step 4: Read the Flag
root@ubuntu:~# cat /root/flag.txtTHM{xxxxxxxxxxxxxxxxxxxxxxxxxxx}Step 5: The sha256sum Moment
While still in the root shell, verify the file just exploited against its expected on-disk hash:
root@ubuntu:~# sha256sum /usr/bin/suc4d2e053445c5f89d13b68bb54de8d67358e1aa20a2b8f0688cb8a47a32edbdf /usr/bin/suThat hash matches a freshly-installed, unmodified system. AIDE, Tripwire, and IMA would all report this file as unmodified. A file integrity scan run at this moment returns clean.
The exploit only modified the in-memory page cache copy. The on-disk binary at /usr/bin/su was never written to. File integrity tools read from disk, compute the hash from disk, and compare against a baseline taken from disk. None of that touches the page cache. The corruption used to gain root is invisible to every filesystem-based integrity check.
This is a core property of the vulnerability rather than an incidental side effect. Any detection strategy that relies on watching for changes to setuid binaries will miss this exploit entirely.
Note: Copy Fail is not the first exploit to bypass file integrity monitoring this way. Dirty Pipe (CVE-2022-0847) had the same property. The page cache as an attack surface for file integrity bypass is a recurring theme that predates both CVEs. What distinguishes Copy Fail is its reliability. The absence of a race condition means an attacker can run it once and expect it to work.
Step 6: Exit the Root Shell
exitWhen the root shell was spawned, execution passed through the corrupted page cache. As part of cleanup, the PoC calls posix_fadvise(POSIX_FADV_DONTNEED) on /usr/bin/su. This hints to the kernel that those pages are no longer needed, and the kernel evicts them from the page cache. The next time any process reads /usr/bin/su, the kernel reloads the original, clean binary from disk.
For a defender, this means the exploitation window is extremely narrow. The corrupted pages exist in memory only between the last recvmsg() call and the POSIX_FADV_DONTNEED cleanup, a window measured in seconds. By the time a filesystem-based alert fires and a responder opens a terminal, the page cache already shows the original binary. There is nothing to find on disk and nothing in the current page cache. Detection must come from observing the exploitation as it happens, which is covered in Task 4.
Note: Re-running the PoC in Task 4 to verify the mitigation blocks it is supported. The cleanup step evicted the corrupted pages, so the next execution of /usr/bin/su loads cleanly from disk.
Answer the questions below
What is the content of /root/flag.txt?
Ans: THM{copy_fail_kernel_lpe}
Task 4: Detection and Remediation
This task walks through detecting and mitigating the Copy Fail exploit (CVE-2026-31431). You learn:
- Detection: Monitoring unusual AF_ALG socket creation (domain 38,
SOCK_SEQPACKET),splice()calls, and rapidrecvmsg()sequences. - Mitigation: Blacklisting the
algif_aeadkernel module on Ubuntu/Debian, or usinggrubbyon RHEL-family systems. - Verification: Confirming the module is blocked and the exploit fails.
The key insight: filesystem integrity checks miss this exploit entirely; only runtime behaviour reveals it.
Question: What command checks whether algif_aead is a loadable module or compiled into the kernel?
Ans: modinfo algif_aead
Takeaways
The Linux page cache is shared infrastructure. One process writing to it affects every process sharing the same kernel, including containers that appear to be fully isolated by namespace and capability restrictions. Isolation at the namespace layer does not extend down to the page cache.
Exploits with no race condition and no kernel offsets change the operational risk window. Public reimplementations of Copy Fail in C, Rust, Go, and arm64 surfaced on GitHub within days of disclosure, alongside the original Python release. Weapons this reliable are used quickly, not gradually.
File integrity tools hash from disk, while this exploit writes to memory. For this class of vulnerability, detection requires syscall-level monitoring, whether through auditd rules watching socket(AF_ALG) calls, eBPF-based tools such as Falco tracking process behaviour, or kernel-level telemetry correlating the recvmsg() volume with the subsequent execve(). Filesystem monitoring on its own is not sufficient.
The kernel update to 6.18.22, 6.19.12, or 7.0 should be applied as soon as the distribution makes it available. The modprobe blacklist is an effective interim control on Ubuntu and Debian, but it is not a substitute for patching.
Next reads
View all →4 Sept
Symbol Tables: What Function Names Actually Are
Function names in a binary are just entries in a table. Two tables actually: .symtab and .dynsym. Here's what each is for, how the struct works, and how to resolve a name from an address.
3 Sept
Building a Custom ELF Loader from Scratch, in C
You type ./program and the kernel loads it. Here's how that works: we build a userspace ELF loader in ~300 lines of C that maps PT_LOAD segments, builds a stack by hand, and jumps to the entry point.
1 Sept
Parsing ELF Binaries in C
The first step to actually understanding what runs on your machine: read an ELF file yourself. No libelf, no readelf — just mmap and pointer casts. I walk the header and section table of a real binary, and diff my output against the tools that already exist.
Get posts by email
One email when I publish, not a drip, not weekly. Sign up and I'll only write when there's something new.
You won't get mail just for signing up. Unsubscribe any time.