AF_ALG page-cache write Copy Fail (CVE-2026-31431)¶
Kernel'in
authencesnAEAD path'indeki bir logic flaw, unprivileged bir kullanıcının AF_ALG +splice()üzerinden, okunabilir herhangi bir dosyanın page cache'ine deterministik 4-byte'lık bir write bırakmasına izin verir — bir setuid binary'sinin cache'lenmiş page'lerini bozarak root elde etmek için.
Mechanism¶
Bir AEAD scratch write neden page cache'e düşer
Linux, crypto algoritmalarını userspace'e AF_ALG soketleri
aracılığıyla açar (AEAD cipher'lar için algif_aead). Bir AEAD
isteğinin, sözleşmesel boyutu assoclen + cryptlen + taglen olan bir
output bölgesi vardır — kernel'in yalnızca o destination scatter-gather
list'in (req->dst) içine write yapması beklenir.
İki olgu bir araya gelerek bug'ı oluşturur:
- 2017'deki bir optimizasyon (commit
72548b093ee3),req->src = req->dstatayıp tag page'lerinisg_chain()ile scatterlist'e zincirleyerekalgif_aead'i in place çalışır hale getirdi. - IPsec Extended Sequence Numbers'ı destekleyen
authencesn(...)template'i, ESN field'ını yeniden düzenlemek için scratch alan olarakassoclen + cryptlenoffset'inde deterministik 4-byte'lık bir write yapar. Bu 4 byte'ın kendi output buffer'ının slack'i içinde kaldığını varsayar.
İhlal edilen invariant şudur: "AEAD output'u asla caller'ın
destination buffer'ının ötesine uzanmaz." splice() ile bir
dosyanın page-cache page'leri isteğe beslendiğinde, o page'ler
zincirlenmiş destination scatterlist'in parçası olur. authencesn
scratch write'ı bu durumda saldırganın yalnızca read erişimi olduğu
bir dosyanın page-cache page'i içinde kontrollü bir offset'e düşer —
dosya izinlerini tamamen bypass ederek. Bırakılan 4 byte
attacker-controlled'dır (isteğin AAD bölgesinden gelirler).
Page cache, executable'ların bellek içi imajını tuttuğu için, bir
setuid-root binary'sinin (örn. /usr/bin/su) cache'lenmiş
page'lerini bozmak, bir sonraki çalıştırılışında root privilege ile
koşan kodu değiştirir — diske hiç write yapmadan. Dirty Pipe'tan veya
çoğu UAF'tan farklı olarak bu, race içermeyen düz bir logic
flaw'dır, dolayısıyla distribution'lar arasında güvenilir biçimde
tetiklenir. Kavramsal olarak Dirty Pipe'a
komşudur: ikisi de read-only bir dosyanın page cache'ini yazılabilir bir
hedefe çevirir, yalnızca write'ı gerçekleştiren primitive'de ayrışırlar.
Walkthrough¶
Public PoC (~732 byte'lık bir Python script olarak raporlanmış) üç primitive'i zincirler. Aşağıdaki yapı kavramsaldır; sahip olduğunuz sistemler üzerinde yetkili araştırma içindir.
1. Bind an AF_ALG AEAD socket to authencesn¶
int tfm = socket(AF_ALG, SOCK_SEQPACKET, 0);
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "aead",
.salg_name = "authencesn(hmac(sha256),cbc(aes))",
};
bind(tfm, (struct sockaddr *)&sa, sizeof(sa));
// set a key, then accept an operation socket
setsockopt(tfm, SOL_ALG, ALG_SET_KEY, key, keylen);
int op = accept(tfm, NULL, NULL);
2. Splice target page-cache pages into the destination¶
Victim setuid binary'sini read-only açın ve onun page-cache page'lerini
splice() ile AEAD operasyonunun pipeline'ına sokun, böylece in-place
src/dst scatterlist'in parçası olurlar:
int f = open("/usr/bin/su", O_RDONLY);
int p[2]; pipe(p);
// load the page-cache page(s) at the target file offset into the pipe...
splice(f, &target_off, p[1], NULL, PAGE_SIZE, 0);
// ...then splice the pipe into the AF_ALG op socket as request data
splice(p[0], NULL, op, NULL, PAGE_SIZE, SPLICE_F_MORE);
3. Trigger the scratch write with controlled bytes¶
AEAD operasyonunu çalıştırın. authencesn scratch write'ının
assoclen + cryptlen offset'inde bıraktığı 4 byte AAD'den alınır —
analizdeki ifadeyle, "AAD byte 4-7, authencesn scratch write'ının hedef
page'e bırakacağı 4-byte'lık değeri sağlar":
// sendmsg() carries control data: ALG_SET_OP (encrypt), ALG_SET_AEAD_ASSOCLEN
// with AAD whose bytes 4..7 are the 4 bytes to plant into the page-cache page.
sendmsg(op, &msg /* with cmsg ALG_SET_OP + ALG_SET_AEAD_ASSOCLEN + AAD */, 0);
recvmsg(op, &out_msg, 0); // forces the out-of-bounds 4-byte write
4. Result¶
Seçilen 4 byte artık /usr/bin/su'nun cache'lenmiş page'inde duruyor.
Bunları bir privilege-dropping kontrolünü etkisiz hale getirecek (ya da
zaten map'lenmiş attacker koduna yönlendirecek) şekilde hazırlamak, bir
sonraki çağrının root ile koşması anlamına gelir:
Etkilenen / düzeltilen sürümler (vendor advisory'lerinden)
- Giriş: in-place optimizasyon commit'i
72548b093ee3(2017). - Raporlanan vulnerable aralıklar: Linux 4.14'ten 7.0-rc'ye kadar; 6.18.22'den önceki 6.18.x; 6.19.12'den önceki 6.19.x.
- Düzeltildi:
fafe0fa2995aile biten commit serisi (Nisan 2026 başı); yamalı kernel'ler 7.0, 6.19.12, 6.18.22'yi içerir. 2026-04-29'da açıklandı, CVSS 7.8 (local, low-priv, UI yok). - Etkilenen distribution'lar arasında Ubuntu 24.04, Amazon Linux 2023, RHEL 10.1, SUSE 16 (ve modülü gönderen diğerlerinin çoğu) bulunur.
Detection¶
- Bilinen disk-encryption araçları (
cryptsetup,systemd-cryptsetup,kcapi-*) dışındaki process'lerden gelen AF_ALGSOCK_SEQPACKETsocket oluşturma anomaliktir; vendor Falco/runtime kuralları tam olarak bunu yakalar. - Setuid binary page cache'inin beklenmedik şekilde değiştirilmesini ve
AF_ALG +
splice()aktivitesini takip eden privilege geçişlerini gözleyin.
Mitigation¶
- Düzeltilmiş bir kernel'e (7.0, 6.19.12, 6.18.22 veya distro backport'ları) yama yapın.
- Geçici çözüm:
algif_aeadmodülünü modprobe ile blacklist'e alın ya da AF_ALG socket oluşturmayı seccomp ile bloklayın;CONFIG_CRYPTO_USER_API_AEAD'i devre dışı bırakmak, ona ihtiyaç duymayan build'ler için interface'i kaldırır.