.
├── 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 # 正确且带注释,便于理解
三个单独无害的提交,组合后产生了漏洞:
| 时间 | 提交 | 影响 |
|---|---|---|
| 2011 | a5079d084f8b | authencesn 被添加,使用 dst scatterlist 作为 ESN 重排列的临时空间。此时无害——因旧 AEAD 接口保持 AAD 分离,且唯一调用方是内核 xfrm。 |
| 2015 | 104880a6b470 | authencesn 转换为新 AEAD 接口,引入了"越过输出边界"在 assoclen+cryptlen 处写入 seqno_lo 的行为。仍不可利用——因 AF_ALG 使用 out-of-place 模式,req->src 和 req->dst 分离,page cache 页仅在只读的 src 中。 |
| 2017 | 72548b093ee3 | AF_ALG in-place 优化。将 AAD+密文从 TX SGL 拷贝到 RX buffer,但通过 sg_chain 链入 tag 页。设置 req->src = req->dst。现在 splice 引入的 page cache 页同时位于可写的 dst scatterlist 中。漏洞形成。 |
splice(文件 → pipe → AF_ALG)
↓
af_alg_sendmsg: 文件页缓存页 → TX SGL (零拷贝)
↓
_aead_recvmsg: in-place 优化
├─ memcpy_sglist: AAD+密文拷贝到 RX SGL
├─ sg_chain: tag 页(仍指向文件页缓存)链入 RX SGL
└─ req->src = req->dst (= 含文件页缓存页的 scatterlist)
↓
crypto_authenc_esn_decrypt: ESN 重排列
└─ scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1)
↓ 在 dst scatterlist 中遍历到 tag 位置
↓ kmap_local_page() 映射文件页缓存页
↓ memcpy: 将 seqno_lo 写入文件页缓存页
↓
HMAC 验证失败 → 返回 -EBADMSG
↓
文件页缓存页已被修改,内核未标记脏页,磁盘文件不变
| 发行版 | 内核版本 | 受影响 |
|---|---|---|
| Ubuntu 24.04 LTS | 6.17.0-1007-aws | 是 |
| Amazon Linux 2023 | 6.18.8-9.213.amzn2023 | 是 |
| RHEL 10.1 | 6.12.0-124.45.1.el10_1 | 是 |
| SUSE 16 | 6.12.0-160000.9-default | 是 |
论文声称自 2017 年以来"几乎所有 Linux 发行版"均受影响。页缓存跨容器共享,因此该漏洞既是本地提权 (LPE) 也是容器逃逸向量。
内核主线的修复提交 a664bf3d603d 将 AF_ALG AEAD 操作回退到 out-of-place(分离 src 和 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)
提交消息:"There is no benefit in operating in-place in algif_aead since the source and destination come from different mappings。"
应急缓解措施:
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead
或通过 seccomp 策略禁止 AF_ALG 套接字创建 (socket(AF_ALG, ...))。
| 特性 | 源码验证 | 说明 |
|---|---|---|
| authencesn 写入 seqno_lo 到末尾 | authencesn.c:134 | 在 assoclen+cryptlen 处写入的是 seqno_lo(低 32 位)。用户描述正确。 |
| ESN 重排列约定 | 博客确认 (xint.io) | AAD bytes 0-3 = seqno_hi, bytes 4-7 = seqno_lo。tmp[1] (=bytes 4-7=seqno_lo) 被写到末尾。 |
| 解密路径不恢复覆盖的字节 | authencesn.c:270-273 vs 215-217 | decrypt_tail 恢复 ESN 到 offset 0-7 但从不恢复 assoclen+cryptlen 处的原始字节。加密路径 (genicv_tail) 则会在写入 ICV 前恢复。 |
| AF_ALG 就地加解密 | algif_aead.c:189-252 | rsgl_src 与 areq->first_rsgl.sgl.sgt.sgl 指向同一 RX SGL。 |
| AF_ALG tag 链入 | algif_aead.c:238-244 | 解密时 tag 通过 sg_chain 引用而非拷贝——splice 场景下 tag 页仍指向文件页缓存。安全漏洞根因。 |
| splice 零拷贝传输 | fs/splice.c:876, af_alg.c:1049, lib/scatterlist.c:1167 | 直接引用 pipe page,零拷贝。 |
| CVE-2026-31431 | 三条提交链 | authencesn(2011/2015) + in-place(2017) + splice 零拷贝 = 可控制的页缓存写原语。 |