
Reproduktionsanalyse von CVE-2026-31431, in C angepasster Exploit.
.
├── container
│ ├── container.md # Ansätze für Container-Escape
│ ├── expenv # Container-Umgebung
│ │ ├── bin
│ │ │ ├── gscontainer
│ │ │ └── share
│ │ ├── Dockerfile
│ │ ├── run.sh
│ │ └── src
│ │ ├── gscontainer.c
│ │ └── share.c
│ ├── images
│ │ ├── container.png
│ │ ├── getshell.png
│ │ ├── host.png
│ │ ├── payload.png
│ │ └── share.png
│ ├── recode.bin # Einphasiges Shellcode
│ └── stager
│ ├── shell.elf # Zweiphasiges Shellcode, eingebettet in den Exploit
│ ├── stager1.bin
│ ├── stager2.bin
│ ├── stager3.bin
│ └── stager4.bin
├── doc
│ ├── cve-2026-31431.md # xint.io Analysezusammenfassung
│ └── shellcode.md # Einführung in Shellcode
├── exp.py # curl http://copy.fail/exp -o exp.py
├── README.md
├── success
│ ├── code.py # Aus exp.py extrahiertes Shellcode
│ └── exploit.c # In C minimierter Exploit, enthält natives msf-Privilege-Escalation-Shellcode (muss selbst entschärft werden)
└── test
├── exptest.c # Kommentare entfernt
└── test.c # Korrekt mit Kommentaren, zum leichteren Verständnis
Drei einzeln harmlose Commits ergeben nach Kombination eine Schwachstelle:
splice(Datei → Pipe → AF_ALG)
↓
af_alg_sendmsg: Seiten der Datei-Page-Cache → TX SGL (Zero-Copy)
↓
_aead_recvmsg: In-Place-Optimierung
├─ memcpy_sglist: Kopiert AAD+Chiffretext in RX SGL
├─ sg_chain: Tag-Seite (zeigt immer noch auf Datei-Page-Cache) in RX SGL eingekettet
└─ req->src = req->dst (= scatterlist, das die Page-Cache-Seiten der Datei enthält)
↓
crypto_authenc_esn_decrypt: ESN-Neuanordnung
└─ scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1)
↓ Iteriert durch dst scatterlist bis zur Tag-Position
↓ kmap_local_page() bildet Page-Cache-Seite der Datei ab
↓ memcpy: Schreibt seqno_lo in die Page-Cache-Seite der Datei
↓
HMAC-Verifikation schlägt fehl → Rückgabe -EBADMSG
↓
Die Page-Cache-Seite der Datei wurde verändert, der Kernel markiert die Seite nicht als schmutzig, die Datei auf der Festplatte bleibt unverändert
Das Paper behauptet, dass seit 2017 "nahezu alle Linux-Distributionen" betroffen sind. Da der Page-Cache über Container hinweg gemeinsam genutzt wird, ist die Schwachstelle sowohl ein lokaler Privilege-Escalation- (LPE) als auch ein Container-Escape-Vektor.
Der Kernel-Mainline-Fix-Commit a664bf3d603d setzt AF_ALG-AEAD-Operationen auf Out-of-Place (getrennte src und dst) zurück:
Before: aead_request_set_crypt(..., rsgl_src,
areq->first_rsgl.sgl.sgt.sgl, ...)
^ src == dst ^
After: aead_request_set_crypt(..., tsgl_src,
areq->first_rsgl.sgl.sgt.sgl, ...)
^ src ≠ dst ^ (TX SGL für src, RX SGL für dst)
Commit-Nachricht: "There is no benefit in operating in-place in algif_aead since the source and destination come from different mappings."
Sofortmaßnahme:
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead
Oder via Seccomp-Richtlinie das Erstellen von AF_ALG-Sockets verbieten (socket(AF_ALG, ...)).
| Zeit | Commit | Auswirkung |
|---|
| 2011 | a5079d084f8b | authencesn wird hinzugefügt, verwendet dst scatterlist als temporären Speicher für die ESN-Neuanordnung. Zu diesem Zeitpunkt harmlos – da das alte AEAD-Interface AAD getrennt hält und der einzige Aufrufer der Kernel-xfrm ist. |
| 2015 | 104880a6b470 | authencesn wird auf das neue AEAD-Interface umgestellt, was das Schreiben von seqno_lo "über die Ausgabegrenze" an assoclen+cryptlen einführt. Noch nicht ausnutzbar – da AF_ALG den Out-of-Place-Modus verwendet, sind req->src und req->dst getrennt, und die Page-Cache-Seiten befinden sich nur im schreibgeschützten src. |
| 2017 | 72548b093ee3 | AF_ALG In-Place-Optimierung. Kopiert AAD+Chiffretext von TX SGL in RX-Puffer, verknüpft aber das Tag-Blatt via sg_chain. Setzt req->src = req->dst. Nun befinden sich die per splice eingeführten Page-Cache-Seiten auch im beschreibbaren dst scatterlist. Die Schwachstelle entsteht. |
| Distribution | Kernel-Version | Betroffen |
|---|
| Ubuntu 24.04 LTS | 6.17.0-1007-aws | Ja |
| Amazon Linux 2023 | 6.18.8-9.213.amzn2023 | Ja |
| RHEL 10.1 | 6.12.0-124.45.1.el10_1 | Ja |
| SUSE 16 | 6.12.0-160000.9-default | Ja |
| Eigenschaft | Quellcode-Verifizierung | Beschreibung |
|---|
| authencesn schreibt seqno_lo ans Ende | authencesn.c:134 | An assoclen+cryptlen wird seqno_lo (niedrige 32 Bit) geschrieben. Die Beschreibung ist korrekt. |
| ESN-Neuanordnungskonvention | Blog bestätigt (xint.io) | AAD-Bytes 0-3 = seqno_hi, Bytes 4-7 = seqno_lo. tmp[1] (=Bytes 4-7=seqno_lo) wird ans Ende geschrieben. |
| Entschlüsselungspfad stellt überschriebene Bytes nicht wieder her | authencesn.c:270-273 vs 215-217 | decrypt_tail stellt ESN an Offset 0-7 wieder her, aber nie die ursprünglichen Bytes an assoclen+cryptlen. Der Verschlüsselungspfad (genicv_tail) stellt sie vor dem Schreiben von ICV wieder her. |
| AF_ALG In-Place-Verschlüsselung/Entschlüsselung | algif_aead.c:189-252 | rsgl_src und areq->first_rsgl.sgl.sgt.sgl zeigen auf denselben RX SGL. |
| AF_ALG Tag-Verkettung | algif_aead.c:238-244 | Bei der Entschlüsselung wird das Tag per sg_chain referenziert, nicht kopiert – bei Splice zeigt die Tag-Seite immer noch auf den Datei-Page-Cache. Ursache der Sicherheitslücke. |
| Splice Zero-Copy-Übertragung | fs/splice.c:876, af_alg.c:1049, lib/scatterlist.c:1167 | Referenziert direkt Pipe-Seiten, keine Kopie. |
| CVE-2026-31431 | Kette aus drei Commits | authencesn(2011/2015) + in-place(2017) + Splice Zero-Copy = steuerbare Page-Cache-Schreibprimitive. |