本地一个低权限程序,构造一次特制的 HTTP 响应,Windows 内核就把响应头的总长度算错了。四个数加起来正好 4 GB,32 位变量装不下,存进去就是 0。http.sys 按 0 开缓冲区,末尾多写出去 48 字节。

48 字节,不多。公开的验证代码在 Windows 11 25H2 上已经稳定拿到 SYSTEM。微软 8 月 11 日的月度更新修掉了。

一、漏洞概况

CVE 编号

CVE-2026-62735

漏洞类型

整数溢出 → 堆溢出 → 本地提权

影响组件

内核驱动 http.sys

触发入口

HTTP Server API 的响应路径。多值响应头是公开文档里的功能,禁不掉

定级

微软 7.8(Important),ZDI 给 8.8,差在作用域怎么算

利用条件

本地低权限代码执行。http.sys 默认手动启动,调一下初始化函数就能拉起;非管理员可注册 1024 以上端口

攻击结果

SYSTEM 权限

在野情况

暂未发现在野。微软评估更可能被利用

公开状态

2026-08-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 号。

产品线

修复后 Build

补丁

Windows Server 2012 / 2012 R2

6.2.9200.26280 / 6.3.9600.23338

KB5120386 / KB5120385

Server 2016 / Windows 10 1607

10.0.14393.9418

KB5120418

Server 2019 / Windows 10 1809

10.0.17763.9121

KB5120238

Windows Server 2022

10.0.20348.5499(热补丁 5440)

KB5120242 / KB5120229

Windows 10 21H2 / 22H2

10.0.19044.7663 / 10.0.19045.7663

KB5120249

Windows 11 23H2

10.0.22631.7517

KB5120240

Windows Server 2025

10.0.26100.33296(热补丁 33222)

KB5120233 / KB5120228

Windows 11 24H2

10.0.26100.9168(热补丁 9106)

KB5121003 / KB5120994

Windows 11 25H2

10.0.26200.9168(热补丁 9106)

KB5121003 / KB5120994

Windows 11 26H1

10.0.28000.2704

KB5121000

五、处置建议

  1. 打 8 月累积更新。这是唯一能根治的办法,Build 号照上面的表对。

  2. 装不上完整补丁的,优先上热补丁,不用重启。