tgies/copy-fail-c
tgies/copy-fail-cCross-platform C port of the Copy Fail Linux LPE (CVE-2026-31431). Disclosed 2026-04-29 by Theori / Xint.
From the README
Copy Fail (CVE-2026-31431) - C port
English (en) ∙ 日本語 (ja) ∙ 简体中文 (zh-cn) ∙ 한국어 (ko) ∙ Русский (ru)
A cross-platform C reimplementation of the Copy Fail Linux LPE (CVE-2026-31431), disclosed 2026-04-29 by Theori / Xint. See the canonical writeup at copy.fail for the full vulnerability description, timeline, and Theori's discovery process.
The publicly-released proof-of-concept is a 732-byte Python script. This C port demonstrates that the same exploit can be expressed as portable C compilable to any architecture nolibc supports, with no per-arch hex blobs or inline assembly in the project's own source.
Author of this port: Tony Gies . Discovery and original disclosure: Theori / Xint.
Repository layout
copy-fail-c/
├── exploit.c the dropper (binary-mutation variant)
├── exploit-passwd.c the dropper (/etc/passwd UID-flip variant)
├── vulnerable.c non-destructive vulnerability checker
├── payload.c the body that gets dropped (setgid+setuid+execve sh)
├── utils.c, utils.h shared AF_ALG/splice page-cache mutation primitive
├── Makefile build orchestration
├── nolibc/ vendored from torvalds/linux tools/include/nolibc
└── README.md this file
After make:
├── payload tiny static ELF, embedded into the dropper as bytes
├── payload.o payload wrapped as a relocatable .o by `ld -r -b binary`
├── exploit dropper, binary-mutation variant
├── exploit-passwd dropper, /etc/passwd UID-flip variant
└── vulnerable non-destructive vulnerability checker
exploit.c opens the target binary read-only, then for each 4-byte window of
the embedded payload runs one bogus AEAD-decrypt through AF_ALG whose
ciphertext input is supplied via splice() from the target's page-cache pages.
The authencesn template's in-place optimization treats the splice'd source
pages as both the ciphertext input and the plaintext destination, so the
(failing) decrypt has already overwritten the page-cache page by the time
authentication verification rejects the request. After 4 * N iterations the
target's cached image has been replaced byte-for-byte with the payload.
execve()'ing the target loads the mutated pages; the on-disk inode is still
setuid root, so the kernel grants root credentials and runs the payload.
payload.c is plain portable C: setgid(0); setuid(0); execve("/bin/sh", ...). nolibc supplies the _start, the syscall machinery, and the per-arch
register-juggling.
A second variant, exploit-passwd.c, mutates four bytes of /etc/passwd's page
cache instead of a setuid binary's image. It needs no embedded payload and
works on systems where the binary-mutation route is blocked, but its cashout
surface is much narrower.
vulnerable.c is not an exploit. It creates a local testfile containing the
string init, then runs the same patch_chunk() primitiv