
Analisi di riproduzione per CVE-2026-31431, exp adattato in C.
.
├── container
│ ├── container.md # 容器逃逸的思路
│ ├── expenv # 容器环境
│ │ ├── bin
│ │ │ ├── gscontainer
│ │ │ └── share
│ │ ├── Dockerfile
│ │ ├── run.sh
│ │ └── src
│ │ ├── gscontainer.c
│ │ └── share.c
│ ├── images
│ │ ├── container.png
│ │ ├── getshell.png
│ │ ├── host.png
│ │ ├── payload.png
│ │ └── share.png
│ ├── recode.bin # 单阶段的 shellcode
│ └── stager
│ ├── shell.elf # 内嵌在exp当中的分阶段 shellcode
│ ├── stager1.bin
│ ├── stager2.bin
│ ├── stager3.bin
│ └── stager4.bin
├── doc
│ ├── cve-2026-31431.md # xint.io 分析总结
│ └── shellcode.md # 介绍什么是 shellcode
├── exp.py # curl http://copy.fail/exp -o exp.py
├── README.md
├── success
│ ├── code.py # exp.py 提取的 shellcode
│ └── exploit.c # C 语言精简的 exp,当中有原生的 msf 提权的 shellcode,需要自己免杀
└── test
├── exptest.c # 清除注释
└── test.c # 正确且带注释,便于理解
Tre commit singolarmente innocui, combinati, hanno prodotto la vulnerabilità:
| Tempo | Commit | Impatto |
|---|---|---|
| 2011 | a5079d084f8b | authencesn è stato aggiunto, utilizzando la dst scatterlist come spazio temporaneo per il riordinamento ESN. Innocuo a questo punto — poiché la vecchia interfaccia AEAD manteneva AAD separato, e l'unico chiamante era lo xfrm del kernel. |
| 2015 | 104880a6b470 | authencesn è stato convertito alla nuova interfaccia AEAD, introducendo il comportamento di "scrivere oltre il limite di output" a assoclen+cryptlen per seqno_lo. Ancora non sfruttabile — poiché AF_ALG usava la modalità out-of-place, req->src e req->dst erano separati, e le pagine della cache di pagina erano solo nella sorgente di sola lettura. |
| 2017 | 72548b093ee3 | Ottimizzazione in-place di AF_ALG. Copia AAD+ciphertext dal TX SGL al buffer RX, ma concatena la pagina del tag tramite sg_chain. Imposta req->src = req->dst. Ora le pagine della cache di pagina introdotte da splice si trovano anche nel dst scatterlist scrivibile. La vulnerabilità si forma. |
splice(file → pipe → AF_ALG)
↓
af_alg_sendmsg: pagine cache di pagina del file → TX SGL (copia zero)
↓
_aead_recvmsg: ottimizzazione in-place
├─ memcpy_sglist: copia AAD+ciphertext nel RX SGL
├─ sg_chain: la pagina del tag (ancora puntata alla cache di pagina del file) viene concatenata nel RX SGL
└─ req->src = req->dst (= scatterlist contenente le pagine cache di pagina del file)
↓
crypto_authenc_esn_decrypt: riordinamento ESN
└─ scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1)
↓ attraversa il dst scatterlist fino alla posizione del tag
↓ kmap_local_page() mappa la pagina cache di pagina del file
↓ memcpy: scrive seqno_lo nella pagina cache di pagina del file
↓
Verifica HMAC fallita → restituisce -EBADMSG
↓
La pagina cache di pagina del file è stata modificata, il kernel non ha marcato la pagina come sporca, il file su disco rimane invariato
| Distribuzione | Versione del kernel | Colpito |
|---|---|---|
| Ubuntu 24.04 LTS | 6.17.0-1007-aws | Sì |
| Amazon Linux 2023 | 6.18.8-9.213.amzn2023 | Sì |
| RHEL 10.1 | 6.12.0-124.45.1.el10_1 | Sì |
| SUSE 16 | 6.12.0-160000.9-default | Sì |
L'articolo afferma che "quasi tutte le distribuzioni Linux" dal 2017 sono colpite. La cache di pagina è condivisa tra i contenitori, quindi questa vulnerabilità è sia un vettore di escalation locale (LPE) che di fuga dal contenitore.
Il commit di correzione nel kernel principale a664bf3d603d riporta le operazioni AEAD di AF_ALG alla modalità out-of-place (src e dst separati):
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 for src, RX SGL for dst)
Messaggio del commit: "Non c'è alcun vantaggio nell'operare in-place in algif_aead poiché la sorgente e la destinazione provengono da mapping diversi."
Misure di mitigazione di emergenza:
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead
Oppure tramite una politica seccomp che blocchi la creazione di socket AF_ALG (socket(AF_ALG, ...)).
| Caratteristica | Verifica del codice sorgente | Descrizione |
|---|---|---|
| authencesn scrive seqno_lo alla fine | authencesn.c:134 | Scrive seqno_lo (32 bit inferiori) a assoclen+cryptlen. Descrizione utente corretta. |
| Convenzione di riordinamento ESN | Confermato dal blog (xint.io) | AAD bytes 0-3 = seqno_hi, bytes 4-7 = seqno_lo. tmp[1] (=bytes 4-7=seqno_lo) viene scritto alla fine. |
| Il percorso di decrittazione non ripristina i byte sovrascritti | authencesn.c:270-273 vs 215-217 | decrypt_tail ripristina ESN all'offset 0-7 ma non ripristina mai i byte originali a assoclen+cryptlen. Il percorso di crittografia (genicv_tail) invece ripristina prima di scrivere ICV. |
| Crittografia/decrittografia in-place di AF_ALG | algif_aead.c:189-252 | rsgl_src punta allo stesso RX SGL di areq->first_rsgl.sgl.sgt.sgl. |
| Concatenamento del tag in AF_ALG | algif_aead.c:238-244 | In decrittazione, il tag è referenziato tramite sg_chain anziché copiato — nello scenario splice, la pagina del tag punta ancora alla cache di pagina del file. Causa principale della vulnerabilità di sicurezza. |
| Trasferimento a copia zero con splice | fs/splice.c:876, af_alg.c:1049, lib/scatterlist.c:1167 | Riferisce direttamente la pagina della pipe, copia zero. |
| CVE-2026-31431 | Tre catene di commit | authencesn(2011/2015) + in-place(2017) + splice a copia zero = primitiva di scrittura controllabile nella cache di pagina. |