
对 CVE-2026-31431 的复现分析、C 改编的 exp。
.
├── container
│ ├── container.md # Container escape ideas
│ ├── expenv # Container environment
│ │ ├── bin
│ │ │ ├── gscontainer
│ │ │ └── share
│ │ ├── Dockerfile
│ │ ├── run.sh
│ │ └── src
│ │ ├── gscontainer.c
│ │ └── share.c
│ ├── images
│ │ ├── container.png
│ │ ├── getshell.png
│ │ ├── host.png
│ │ ├── payload.png
│ │ └── share.png
│ ├── recode.bin # Single-stage shellcode
│ └── stager
│ ├── shell.elf # Staged shellcode embedded in the exploit
│ ├── stager1.bin
│ ├── stager2.bin
│ ├── stager3.bin
│ └── stager4.bin
├── doc
│ ├── cve-2026-31431.md # xint.io analysis summary
│ └── shellcode.md # Introduction to what shellcode is
├── exp.py # curl http://copy.fail/exp -o exp.py
├── README.md
├── success
│ ├── code.py # Shellcode extracted from exp.py
│ └── exploit.c # C-based minimal exploit with native msf privilege escalation shellcode, needs its own bypass
└── test
├── exptest.c # Without comments
└── test.c # Correct and with comments for easier understanding
Three individually harmless commits combined to create the vulnerability:
| Time | Commit | Impact |
|---|---|---|
| 2011 | a5079d084f8b | authencesn added, uses dst scatterlist as temporary space for ESN reordering. Harmless at this point — the old AEAD interface kept AAD separate, and the only caller was the kernel xfrm. |
| 2015 | 104880a6b470 | authencesn converted to the new AEAD interface, introducing the behavior of writing seqno_lo "past the output boundary" at assoclen+cryptlen. Still not exploitable — because AF_ALG uses out-of-place mode, req->src and req->dst are separate, and page cache pages are only in read-only src. |
| 2017 | 72548b093ee3 | AF_ALG in-place optimization. Copies AAD+ciphertext from TX SGL to RX buffer, but chains the tag page via sg_chain. Sets req->src = req->dst. Now page cache pages introduced by splice are also in the writable dst scatterlist. Vulnerability formed. |
splice(file → pipe → AF_ALG)
↓
af_alg_sendmsg: file page cache pages → TX SGL (zero-copy)
↓
_aead_recvmsg: in-place optimization
├─ memcpy_sglist: copy AAD+ciphertext to RX SGL
├─ sg_chain: chain tag page (still pointing to file page cache) into RX SGL
└─ req->src = req->dst (= scatterlist containing file page cache pages)
↓
crypto_authenc_esn_decrypt: ESN reordering
└─ scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1)
↓ traverse dst scatterlist to tag position
↓ kmap_local_page() maps file page cache page
↓ memcpy: writes seqno_lo into file page cache page
↓
HMAC verification fails → returns -EBADMSG
↓
File page cache page modified, kernel does not mark page dirty, disk file unchanged
| Distribution | Kernel Version | Affected |
|---|---|---|
| Ubuntu 24.04 LTS | 6.17.0-1007-aws | Yes |
| Amazon Linux 2023 | 6.18.8-9.213.amzn2023 | Yes |
| RHEL 10.1 | 6.12.0-124.45.1.el10_1 | Yes |
| SUSE 16 | 6.12.0-160000.9-default | Yes |
The paper claims "almost all Linux distributions" since 2017 are affected. Page cache is shared across containers, making this vulnerability both a local privilege escalation (LPE) and a container escape vector.
The upstream fix commit a664bf3d603d reverts AF_ALG AEAD operations to out-of-place (separate src and dst):
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)
Commit message: "There is no benefit in operating in-place in algif_aead since the source and destination come from different mappings."
Emergency mitigation:
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead
Or block AF_ALG socket creation (socket(AF_ALG, ...)) via seccomp policy.
| Feature | Source Verification | Description |
|---|---|---|
| authencesn writes seqno_lo to the end | authencesn.c:134 | Writes seqno_lo (low 32 bits) at assoclen+cryptlen. User description is correct. |
| ESN reordering convention | Blog confirms (xint.io) | AAD bytes 0-3 = seqno_hi, bytes 4-7 = seqno_lo. tmp[1] (=bytes 4-7=seqno_lo) is written to the end. |
| Decryption path does not restore overwritten bytes | authencesn.c:270-273 vs 215-217 | decrypt_tail restores ESN at offset 0-7 but never restores original bytes at assoclen+cryptlen. Encryption path (genicv_tail) restores before writing ICV. |
| AF_ALG in-place encryption/decryption | algif_aead.c:189-252 | rsgl_src points to the same RX SGL as areq->first_rsgl.sgl.sgt.sgl. |
| AF_ALG tag chaining | algif_aead.c:238-244 | During decryption, tag is referenced via sg_chain instead of copied — in splice scenarios, the tag page still points to file page cache. Root cause of security vulnerability. |
| Splice zero-copy transfer | fs/splice.c:876, af_alg.c:1049, lib/scatterlist.c:1167 | Directly references pipe pages, zero-copy. |
| CVE-2026-31431 | Three commit chains | authencesn(2011/2015) + in-place(2017) + splice zero-copy = controllable page cache write primitive. |