
Tiefgehende technische Analyse von CVE-2026-31431 (Copy Fail) im Linux-Kernel, einer lokalen Privilegieneskalation über einen In-Place-Scatterlist-Fehler in AF_ALG, einschließlich Ursachenanalyse, Ausnutzungspfad und Anleitung zur Patch-Einreichung.
Schweregrad: CVSS 7.8 (Hoch)
Klasse: Lokale Privilegieneskalation
Eingeführt: Linux 4.14 (2017)
Behoben: Commit a664bf3d603d
CVE-2026-31431 ist eine Schwachstelle zur lokalen Privilegieneskalation im algif_aead-Modul des Linux-Kernels — der AEAD-Socket-Schnittstelle der Userspace-Crypto-API des Kernels (AF_ALG).
Die Schwachstelle ermöglicht es einem nicht privilegierten Benutzer, die Adressfamilie AF_ALG in der Crypto-API des Linux-Kernels zu nutzen, um kontrollierte 4-Byte-Schreibvorgänge in den Page Cache durchzuführen, der die In-Memory-Kopien von Dateien unterstützt. Durch das Ersetzen von Code in der In-Memory-Kopie einer lesbaren ausführbaren Datei, die im Page Cache gespeichert ist, kann ein Angreifer Benutzerrechte eskalieren, wenn ein privilegierter Prozess später diese korrumpierte Version der Datei ausführt.
Im Gegensatz zu Dirty Cow (CVE-2016-5195), das das Gewinnen einer Race Condition erforderte, ist Copy Fail ein geradliniger Logikfehler. Er wird ohne Races, Wiederholungen oder absturzanfällige Timing-Fenster ausgelöst.
Commit 72548b093ee3 („crypto: algif_aead - switch to in-place processing“) führte eine Optimierung ein, die req->src == req->dst innerhalb von aead_recvmsg() setzte und damit eine separate Ausgabepuffer-Zuweisung eliminierte.
Diese Optimierung führte dazu, dass req->src und req->dst auf eine kombinierte Scatterlist zeigten. Dadurch wurden Page-Cache-Seiten aus dem splice()-Aufruf fälschlicherweise direkt in die beschreibbare Ziel-Scatterlist eingebunden.
Die AEAD-API des Kernels definiert einen klaren Ausgabevertrag: Der Zielpuffer empfängt AAD || plaintext, genau assoclen + (cryptlen - authsize) Bytes. Nichts in der API erzwingt diesen Vertrag, und nichts dokumentiert ihn als Anforderung. Das Design geht davon aus, dass jeder AEAD-Algorithmus seine Schreibvorgänge auf das vorgesehene Ziel beschränkt. Ein Algorithmus tat dies nicht.
crypto/
├── algif_aead.c ← In-Place-Scatterlist-Fehler (primär)
└── authencesn.c ← Out-of-Bounds-Scratch-Schreibvorgang (Auslöser)
crypto/algif_aead.c — Die In-Place-Zuweisung/* VERWUNDBAR — In-Place-Pfad nach 2017 */
static int aead_recvmsg(struct socket *sock, struct msghdr *msg,
size_t ignored, int flags)
{
/* ... */
/*
* Wenn Userspace splice(2) auf einer Datei aufruft, enthält areq->tsgl
* Page-Cache-Seiten dieser Datei. Das Setzen von dst = src übergibt
* diese Page-Cache-Seiten der Crypto-Engine als beschreibbaren
* Ausgabepuffer.
*/
aead_request_set_crypt(req,
areq->tsgl, /* src */
areq->tsgl, /* dst — identischer Zeiger; Grundursache */
areq->cryptlen, iv);
}
crypto/authencesn.c — Der Out-of-Bounds-Scratch-SchreibvorgangDie authencesn-Vorlage ist ein AEAD-Wrapper, der von IPsec für die Unterstützung erweiterter Sequenznummern (ESN) verwendet wird. IPsec verwendet 64-Bit-Sequenznummern, die in eine obere Hälfte (seqno_hi, Bytes 0–3 der AAD) und eine untere Hälfte (seqno_lo, Bytes 4–7) aufgeteilt sind. Das Wire-Format überträgt nur seqno_lo; seqno_hi ist implizit.
/* crypto/authencesn.c — Scratch-Schreibvorgang über die Ausgabegrenze hinaus */
static int crypto_authenc_esn_decrypt(struct aead_request *req)
{
/* ... */
/*
* authencesn speichert seqno_hi vorübergehend am Offset
* (assoclen + cryptlen) in req->dst — ein Wort hinter der
* legitimen Ausgaberegion.
*
* Unter normalem Betrieb: trifft Kernel-eigenen Speicher.
* Mit In-Place-Scatterlist: trifft eine Page-Cache-Seite.
*/
scatterwalk_map_and_copy(&seqno_hi,
req->dst,
req->assoclen + req->cryptlen, /* Out-of-Bounds-Offset */
sizeof(seqno_hi), /* 4 Bytes */
1 /* write */);
/* authencesn stellt diese Bytes auf dem Fehlerpfad nicht wieder her */
}
Upstream-Fix: Commit a664bf3d603d — macht die Optimierung von 2017 rückgängig.
crypto/algif_aead.c — Getrennte Scatterlists/* BEHOBEN — getrennte src und dst */
aead_request_set_crypt(req,
areq->tsgl, /* src — kann Page-Cache-Seiten enthalten; schreibgeschützt */
areq->rsgl, /* dst — frisch zugewiesener Kernel-Puffer; beschreibbar */
areq->cryptlen, iv);
Wirkung: Page-Cache-Seiten bleiben in src, das die Crypto-Engine nur liest. Der authencesn-Scratch-Schreibvorgang bei assoclen + cryptlen landet nun in einem Kernel-zugewiesenen Puffer, der dem Prozess gehört. Keine Page-Cache-Seite ist jemals vom Schreibpfad aus erreichbar.
Das Folgende beschreibt, wie der 4-Byte-Schreibvorgang eine vollständige Privilegieneskalation erreicht.
| Schritt | Aktion |
|---|---|
| 1 | Öffnen eines AF_ALG-Sockets, der für authencesn(hmac(sha256),cbc(aes)) konfiguriert ist |
| 2 | splice() einer lesbaren setuid-Binärdatei (z. B. /usr/bin/sudo) in den Socket — dies platziert ihre Page-Cache-Seiten in areq->tsgl |
| 3 | assoclen + cryptlen so gestalten, dass der Scratch-Schreib-Offset mit einem Zielbyte im gecachten Abbild der Binärdatei übereinstimmt |
| 4 | Aufruf von recvmsg() — löst den Entschlüsselungspfad aus, authencesn schreibt 4 vom Angreifer kontrollierte Bytes in den Page Cache |
| 5 | Die Datei auf der Festplatte ist unverändert; Integritätsprüfer, inotify und inode-Zeitstempel sind nicht betroffen |
| 6 | Jede spätere Ausführung der Binärdatei mappt die bereits modifizierten Page-Cache-Seiten — der gepatchte Code läuft mit euid=0 |
Der Schreibvorgang landet in den gecachten Daten der gespliceten Datei im Speicher und umgeht dabei vollständig die Dateiberechtigungen.
./scripts/get_maintainer.pl crypto/algif_aead.c
# Herbert Xu <[email protected]>
# [email protected]
./scripts/checkpatch.pl 0001-crypto-algif_aead-revert-inplace.patch
crypto: algif_aead - restore separate src/dst scatterlists in recvmsg
Commit 72548b093ee3 ("crypto: algif_aead - switch to in-place
processing") set req->src == req->dst inside aead_recvmsg(). When
the caller uses splice(2), the source scatterlist contains page
cache pages of the spliced file. With the in-place optimization,
those same pages become the writable destination passed to the
AEAD transform.
authencesn writes 4 bytes at offset (assoclen + cryptlen) into
req->dst as scratch space for Extended Sequence Number
rearrangement and does not restore them on the error path. With
page cache pages present in dst, this write bypasses the VFS
permission layer and corrupts the in-memory image of the spliced
file without modifying the on-disk inode.
An unprivileged local user can exploit this to overwrite
arbitrary bytes in the page cache of any readable setuid binary
and escalate privileges to root.
Restore separate src and dst scatterlists so that page cache
pages are confined to the read-only source path.
Fixes: 72548b093ee3 ("crypto: algif_aead - switch to in-place processing")
Cc: [email protected] # 4.14+
Reported-by: Taeyang Lee <[email protected]>
Signed-off-by: Your Name <[email protected]>
git send-emailgit send-email \
[email protected] \
[email protected] \
[email protected] \
0001-crypto-algif_aead-revert-inplace.patch
| Element | Zweck |
|---|---|
subsystem: datei - verbphrase Betreff | Erforderliches Format; unter 72 Zeichen |
Fixes: <sha> ("ursprünglicher Titel") | Löst automatisches Stable-Backport-Tooling aus |
Cc: [email protected] | Markiert den Patch für LTS-Zweig-Backports |
Reported-by: | Namensnennung des Entdeckers |
Signed-off-by: | Developer Certificate of Origin — obligatorisch |
Verwundbare Kernel umfassen Linux 4.14 bis 7.0-rc, alle 6.18.x vor 6.18.22 und 6.19.x vor 6.19.12.
Vorläufige Abhilfe (auf den meisten Systemen kein Neustart erforderlich):
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf
rmmod algif_aead 2>/dev/null || true
Dieser Workaround beeinträchtigt dm-crypt/LUKS, kTLS, IPsec/XFRM, OpenSSL, GnuTLS, NSS oder SSH nicht.
xint.io/blog/copy-fail-linux-distributionsnvd.nist.gov/vuln/detail/CVE-2026-31431a664bf3d603d (Mainline)72548b093ee3 (Linux 4.14, 2017)