文章发于:
基本信息
依赖于ICS服务,Internet Connect Sharing,对应注册表,依赖ipnathlp.dll
HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesSharedAccess
漏洞存在于处理DHCP请求时,由于没有检查边界,导致在使用memset时使用的长度参数来源于数据包内,可以导致栈溢出。服务调试参考第二个参考链接。
影响版本
略
环境搭建
参考https://github.com/ruijanlee/h3cc/blob/master/h3cc_ruijanlee/doc/c8.md,同时加一个Linux,网卡使用第二个网卡,使得Linux发出的DHCP包能够被Windows接收到。
技术分析&调试
静态分析
对比补丁修复前后的逻辑,有两个明显的不同点,有两种产生漏洞的可能的地方。
-
在修复版本中在进行
if ( *((_BYTE *)a2 + 230) > 0x20u )
判断之前先调用了DumpDhcpHeaderInfo
,在漏洞代码中先进行判断在调用DumpDhcpHeaderInfo
-
在修复版本中如果满足
if ( *((_BYTE *)a2 + 230) > 0x20u )
则进入if内,在结束if语句时会通过跳转略过一部分处理逻辑,而在未修复版本内则还会继续处理。
可以看出 a2 + 230
为_NH_BUFFER
结构体内的某个长度字段,该处为判断这个长度字段存储的长度,该漏洞应该是溢出漏洞,并且在产生漏洞的地方需要读取该字段。
所以漏洞应该是第二点所说的,产生在略过的逻辑中。
// 未修复代码void __fastcall DhcpProcessMessage(struct _DHCP_INTERFACE *a1, struct _NH_BUFFER *a2){
......
memset_0(&v12, 0, 0x40ui64);
if ( *((_BYTE *)a2 + 230) > 0x20u )
{
if ( v4 != (CInterfaceMonitor *)&WPP_GLOBAL_Control && (*((_BYTE *)v4 + 28) & 2) != 0 && *((_BYTE *)v4 + 25) >= 4u )
WPP_SF_dD(
*((_QWORD *)v4 + 2),
97i64,
&WPP_2a3aeb8dd77c3a1919c551579bb6cf5d_Traceguids,
*((unsigned __int8 *)a2 + 230),
32);
_InterlockedIncrement((volatile signed __int32 *)&DhcpStatistics);
}
DumpDhcpHeaderInfo(a2);
// 修复代码void __fastcall DhcpProcessMessage(struct _DHCP_INTERFACE *a1, struct _NH_BUFFER *a2){
......
memset_0(&v11, 0, 0x40ui64);
DumpDhcpHeaderInfo(a2);
if ( *((_BYTE *)a2 + 230) > 0x20u )
{
if ( WPP_GLOBAL_Control != (CInterfaceMonitor *)&WPP_GLOBAL_Control
&& (*((_BYTE *)WPP_GLOBAL_Control + 28) & 2) != 0
&& *((_BYTE *)WPP_GLOBAL_Control + 25) >= 4u )
{
WPP_SF_dd(
*((_QWORD *)WPP_GLOBAL_Control + 2),
97i64,
&WPP_df007ca3347434f5610fc5a17e95e0a3_Traceguids,
*((unsigned __int8 *)a2 + 230),
32);
}
goto LABEL_10;
}
LABEL_10:
_InterlockedIncrement((volatile signed __int32 *)&DhcpStatistics);// 这里多了调用 goto LABEL_11;
......
LABEL_11:
EnterCriticalSection(&DhcpInterfaceLock);
if ( *((int *)a1 + 19) < 0 )
{
LeaveCriticalSection(&DhcpInterfaceLock);
略过的代码中,读取了a2参数的代码如下:
if ( DhcpExtractOptionsFromMessage((struct _NH_BUFFER *)((char *)a2 + 228), *((_DWORD *)a2 + 55), &v11) )
.....
if ( !v12 )
{
.....
DhcpProcessBootpMessage(a1, a2, &v11);
goto LABEL_11;
}
.....
if ( DhcpIsLocalHardwareAddress((unsigned __int8 *)a2 + 256, *((unsigned __int8 *)a2 + 230)) )
{
....
}
v7 = *(unsigned __int8 *)(v12 + 2);
if ( v7 == 1 )
{
.....
DhcpProcessDiscoverMessage(a1, a2, &v11);
}
else if ( *(_BYTE *)(v12 + 2) == 3 )
{
......
DhcpProcessRequestMessage(a1, a2, &v11);
}
......
}
if ( !DhcpArpForDad )
{
v10 = *(_DWORD *)(v13 + 2);
DhcpRemoveArpEntry(v10);
DhcpCancelLease(v10, (unsigned __int8 *)a2 + 256, *((unsigned __int8 *)a2 + 230));
.....
}
else
{
if ( *(_BYTE *)(v12 + 2) != 7 )
{
if ( *(_BYTE *)(v12 + 2) == 8 )
{
......
DhcpProcessInformMessage(a1, a2, &v11);
}
else
{
......
}
goto LABEL_11;
}
if ( !DhcpArpForDad )
{
DhcpRemoveArpEntry(*((_DWORD *)a2 + 60));
DhcpCancelLease(*((_DWORD *)a2 + 60), (unsigned __int8 *)a2 + 256, *((unsigned __int8 *)a2 + 230));
}
......
}
查看这些函数代码,在 DhcpProcessBootpMessage函数中有如下逻辑
void __fastcall DhcpProcessBootpMessage( v3 = a2; v5 = (char *)v3 + 228; ...... else
{ if ( !DhcpSendUnicastMessagesEnabled || v5[10] < 0
|| DhcpAddArpEntry(v6, (unsigned __int8 *)v5 + 28, (unsigned __int8)v5[2], v23) )// 这个函数触发了漏洞 {// movzx r8d, byte ptr [r15+2] ; Size ...
前面知道 a2 + 230
是长度字段,v5=v2+228,传入 DhcpAddArpEntry
的size参数为v5+2,也就是a2 + 230
在 DhcpAddArpEntry
函数中,Row为栈内结构体,memcpy传入的长度参数为a2 + 230
,也就是补丁中判断的长度参数。MIB_IPNET_ROW2结构体定义可以在这找到,其大小为0x58
__int64 __fastcall DhcpAddArpEntry(DWORD a1, unsigned __int8 *Src, size_t Size, struct _DHCP_INTERFACE *a4){
MIB_IPNET_ROW2 Row;
......
v4 = (unsigned int)Size;
.....
memset_0(&Row, 0, sizeof(Row));
Row.InterfaceIndex = DhcpAdapterIndex;
Row.Address.Ipv4.sin_family = 2;
Row.Address.Ipv4.sin_addr.S_un.S_addr = a1;
Row.PhysicalAddressLength = v4;
memcpy_0(Row.PhysicalAddress, Src, v4);
......
return v11;
}
}
所以漏洞触发路径为 DhcpProcessMessage->DhcpProcessBootpMessage->DhcpAddArpEntry->memcpy_0
,当长度参数过长时可以利用memcpy触发栈溢出。
动态调试
使用windbg附加到svchost进程,在ipnathlp!DhcpProcessMessage
断点,而后触发DHCP请求,windbg在 ipnathlp!DhcpProcessMessage
断下 由于不知道 DhcpProcessMessage
的a2结构体定义,此处构造正常的DHCP请求,并在调试器中查看这个结构体成员信息。单步运行到判断长度的地方,此时rsi指向传入的 _NH_BUFFER
结构体,
0:004> u
ipnathlp!DhcpProcessMessage+0x7f:
00007ff9`c00176f3 488dbee4000000 lea rdi,[rsi+0E4h]
00007ff9`c00176fa 41b604 mov r14b,4
00007ff9`c00176fd 807f0220 cmp byte ptr [rdi+2],20h
00007ff9`c0017701 7636 jbe ipnathlp!DhcpProcessMessage+0xc5 (00007ff9`c0017739)
00007ff9`c0017703 493bdc cmp rbx,r12
00007ff9`c0017706 742a je ipnathlp!DhcpProcessMessage+0xbe (00007ff9`c0017732)
00007ff9`c0017708 44847b1c test byte ptr [rbx+1Ch],r15b
00007ff9`c001770c 7424 je ipnathlp!DhcpProcessMessage+0xbe (00007ff9`c0017732)
可以在调试器内看到 (_BYTE *)a2 + 230)
值为6
0:004> db rdi+2
00000203`faa1fdb6 06 00 1a cc 8a 61 00 00-80 00 00 00 00 00 00 00 .....a..........
00000203`faa1fdc6 00 00 00 00 00 00 00 00-00 00 00 0c 29 c2 3a 42 ............).:B
00000203`faa1fdd6 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fde6 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fdf6 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fe06 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fe16 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fe26 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
而该处数据来源于DHCP客户端发送的DHCP请求,在wireshark中可以看到数据包中刚好有长度字段值为6,说明(_BYTE *)a2 + 230)处有可能是数据包内的Hardware address length。
此时调用栈:
0:004> k
Child-SP RetAddr Call Site
0000000e`3487f480 00007ff9`c00143a4 ipnathlp!DhcpProcessMessage+0x86
0000000e`3487f540 00007ff9`c0006ecf ipnathlp!DhcpReadCompletionRoutine+0x644
0000000e`3487f5a0 00007ff9`eebe32ea ipnathlp!NhpIoCompletionRoutine+0x6f
0000000e`3487f5d0 00007ff9`eeb22f86 ntdll!RtlpTpIoCallback+0xca
0000000e`3487f610 00007ff9`ee0a7614 ntdll!TppWorkerThread+0x456
0000000e`3487f910 00007ff9`eeb226b1 KERNEL32!BaseThreadInitThunk+0x14
0000000e`3487f940 00000000`00000000 ntdll!RtlUserThreadStart+0x21
此时尝试手动将(_BYTE *)a2 + 230)
修改为0xfe,继续运行,但没有触发异常。
0:004> db rdi+2
00000203`faa1fdb6 06 00 1a cc 8a 61 00 00-80 00 00 00 00 00 00 00 .....a..........
00000203`faa1fdc6 00 00 00 00 00 00 00 00-00 00 00 0c 29 c2 3a 42 ............).:B
00000203`faa1fdd6 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fde6 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fdf6 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fe06 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fe16 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fe26 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0:004> eb rdi+2 fe
0:004> db rdi+2
00000203`faa1fdb6 fe 00 1a cc 8a 61 00 00-80 00 00 00 00 00 00 00 .....a..........
00000203`faa1fdc6 00 00 00 00 00 00 00 00-00 00 00 0c 29 c2 3a 42 ............).:B
00000203`faa1fdd6 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fde6 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fdf6 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fe06 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fe16 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00000203`faa1fe26 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0:002> g
Breakpoint 0 hit
ipnathlp!DhcpProcessMessage:
00007ff9`c0017674 48895c2418 mov qword ptr [rsp+18h],rbx ss:0000000e`346ff630=00000203faa7e350
改为单步调试,再次发起DHCP请求,发现没有进入到漏洞函数 DhcpProcessBootpMe
ssage
中,原因是v13不为0,条件不成立,不会调用 DhcpProcessBootpMessage
if ( !v13 )
{
if ( WPP_GLOBAL_Control != (CInterfaceMonitor *)&WPP_GLOBAL_Control
&& (*((_BYTE *)WPP_GLOBAL_Control + 28) & 2) != 0
&& *((_BYTE *)WPP_GLOBAL_Control + 25) >= 4u )
{
WPP_SF_(*((_QWORD *)WPP_GLOBAL_Control + 2), 98i64, &WPP_2a3aeb8dd77c3a1919c551579bb6cf5d_Traceguids);
}
DhcpProcessBootpMessage(a1, a2, &v12); // 这里触发漏洞
goto LABEL_25;
}
对v13下写断点
0:004> ba w1 rsp+0x38
0:004> g
Breakpoint 6 hit
msvcrt!memset+0x35:
00007ff9`ed1046b5 4983e908 sub r9,8
触发断点,此时调用栈如下,对应代码为 memset_0(a3, 0, 0x40ui64);
0:004> k
Child-SP RetAddr Call Site
0000000e`3487f418 00007ff9`c0015b63 msvcrt!memset+0x35
0000000e`3487f420 00007ff9`c0017754 ipnathlp!DhcpExtractOptionsFromMessage+0x7b
0000000e`3487f480 00007ff9`c00143a4 ipnathlp!DhcpProcessMessage+0xe0
0000000e`3487f540 00007ff9`c0006ecf ipnathlp!DhcpReadCompletionRoutine+0x644
0000000e`3487f5a0 00007ff9`eebe32ea ipnathlp!NhpIoCompletionRoutine+0x6f
0000000e`3487f5d0 00007ff9`eeb22f86 ntdll!RtlpTpIoCallback+0xca
0000000e`3487f610 00007ff9`ee0a7614 ntdll!TppWorkerThread+0x456
0000000e`3487f910 00007ff9`eeb226b1 KERNEL32!BaseThreadInitThunk+0x14
0000000e`3487f940 00000000`00000000 ntdll!RtlUserThreadStart+0x21
此处将目标内存清零,不符合前面说的条件,继续运行,再次触发写断,调用栈为
0:004> k
Child-SP RetAddr Call Site
0000000e`3487f420 00007ff9`c0017754 ipnathlp!DhcpExtractOptionsFromMessage+0x428
0000000e`3487f480 00007ff9`c00143a4 ipnathlp!DhcpProcessMessage+0xe0
0000000e`3487f540 00007ff9`c0006ecf ipnathlp!DhcpReadCompletionRoutine+0x644
0000000e`3487f5a0 00007ff9`eebe32ea ipnathlp!NhpIoCompletionRoutine+0x6f
0000000e`3487f5d0 00007ff9`eeb22f86 ntdll!RtlpTpIoCallback+0xca
0000000e`3487f610 00007ff9`ee0a7614 ntdll!TppWorkerThread+0x456
0000000e`3487f910 00007ff9`eeb226b1 KERNEL32!BaseThreadInitThunk+0x14
0000000e`3487f940 00000000`00000000 ntdll!RtlUserThreadStart+0x21
对应在DhcpExtractOptionsFromMessage
的代码如下,当OptionID为0x35时进入case语句内
v9 = (struct _DHCP_OPTION *)((char *)a1 + 240);
OptionID = v9->OptionID;
if ( OptionID )
{
switch ( OptionID )
case 0x35u:
if ( v6 != (CInterfaceMonitor *)&WPP_GLOBAL_Control
&& (*((_BYTE *)v6 + 28) & 2) != 0
&& *((_BYTE *)v6 + 25) >= 4u )
{
WPP_SF_(*((_QWORD *)v6 + 2), 44i64, &WPP_2a3aeb8dd77c3a1919c551579bb6cf5d_Traceguids);
v6 = WPP_GLOBAL_Control;
}
if ( BYTE1(v9->OptionID) )
{
a3[1] = v9;
}
_DHCP_OPTION
结构体定义如下,对应于DHCP请求内的option
typedef DWORD DHCP_OPTION_ID;
struct _DHCP_OPTION
{
DHCP_OPTION_ID OptionID;
LPWSTR OptionName;
LPWSTR OptionComment;
DHCP_OPTION_DATA DefaultValue;
DHCP_OPTION_TYPE OptionType;
};
0:002> db rdi
35 01 03 3d 07 01 00 0c-29 c2 3a 42 32 04 c0 a8 5..=....).:B2...
89 cd 0c 0f 44 45 53 4b-54 4f 50 2d 54 35 50 37 ....DESKTOP-T5P7
34 45 53 51 12 00 00 00-44 45 53 4b 54 4f 50 2d 4ESQ....DESKTOP-
54 35 50 37 34 45 53 3c-08 4d 53 46 54 20 35 2e T5P74ES<.MSFT 5.
30 37 0e 01 03 06 0f 1f-21 2b 2c 2e 2f 77 79 f9 07......!+,./wy.
根据RFCrfc2132 option 53为传递DHCP消息类型,第一个字节是操作编号,第二个字节恒为1,第三个字节是消息类型,范围是1-9
根据代码,当DHCP中含有option 53一定会进入 DhcpExtractOptionsFromMessage
的 if ( BYTE1(v9->OptionID) )
,把a3[1]
赋值为不为零的值。回到
DhcpProcessMessage
内,v13就不为0,不能进入触发漏洞的逻辑
重新构造DHCP数据包,,删除option53并将 Hardware address length
改为100,单步调试,成功进入到 DhcpAddArpEntry
函数内。
0:004> k
Child-SP RetAddr Call Site
0000000e`3487f2a0 00007ff9`c0016766 ipnathlp!DhcpAddArpEntry+0x14a
0000000e`3487f380 00007ff9`c0017797 ipnathlp!DhcpProcessBootpMessage+0x5ea
0000000e`3487f480 00007ff9`c00143a4 ipnathlp!DhcpProcessMessage+0x123
0000000e`3487f540 00007ff9`c0006ecf ipnathlp!DhcpReadCompletionRoutine+0x644
0000000e`3487f5a0 00007ff9`eebe32ea ipnathlp!NhpIoCompletionRoutine+0x6f
0000000e`3487f5d0 00007ff9`eeb22f86 ntdll!RtlpTpIoCallback+0xca
0000000e`3487f610 00007ff9`ee0a7614 ntdll!TppWorkerThread+0x456
0000000e`3487f910 00007ff9`eeb226b1 KERNEL32!BaseThreadInitThunk+0x14
0000000e`3487f940 00000000`00000000 ntdll!RtlUserThreadStart+0x21
在调试器中可以看到,执行memcpy时长度参数为0x64,继续运行则触发了栈溢出,进程异常退出。
0:005> g
Breakpoint 9 hit
ipnathlp!DhcpAddArpEntry+0x184:
00007ff9`c0012570 e83db80600 call ipnathlp!memcpy (00007ff9`c007ddb2)
0:005> rr8
r8=0000000000000064
0:005> g
STATUS_STACK_BUFFER_OVERRUN encountered
(1858.3b4): Break instruction exception - code 80000003 (first chance)
KERNELBASE!UnhandledExceptionFilter+0x7c:
00007ff9`ec55dd3c cc int 3
0:005> k
Child-SP RetAddr Call Site
0000000e`34b7efa0 00007ff9`c007d096 KERNELBASE!UnhandledExceptionFilter+0x7c
0000000e`34b7f0c0 00007ff9`c007d229 ipnathlp!_raise_securityfailure+0x1a
0000000e`34b7f0f0 00007ff9`c0012600 ipnathlp!_report_gsfailure+0x169
0000000e`34b7f180 00007ff9`c0016766 ipnathlp!DhcpAddArpEntry+0x214
0000000e`34b7f260 00007ff9`c0017797 ipnathlp!DhcpProcessBootpMessage+0x5ea
0000000e`34b7f360 00007ff9`c00143a4 ipnathlp!DhcpProcessMessage+0x123
0000000e`34b7f420 00007ff9`c0006ecf ipnathlp!DhcpReadCompletionRoutine+0x644
0000000e`34b7f480 00007ff9`eebe32ea ipnathlp!NhpIoCompletionRoutine+0x6f
0000000e`34b7f4b0 00007ff9`eeb22f86 ntdll!RtlpTpIoCallback+0xca
0000000e`34b7f4f0 00007ff9`ee0a7614 ntdll!TppWorkerThread+0x456
0000000e`34b7f7f0 00007ff9`eeb226b1 KERNEL32!BaseThreadInitThunk+0x14
0000000e`34b7f820 00000000`00000000 ntdll!RtlUserThreadStart+0x21
0:005> g
ntdll!NtWaitForWorkViaWorkerFactory+0x14:
00007ff9`eeb70aa4 c3 ret
wireshrk中可以看到数据包协议为Bootp。
PoC参考简单实现的DHCP Client并将option 53注释,将 Hardware address length
改为0x100。这个栈溢出长度和内容均为内容可控
小结
这个漏洞起源于memcpy时src和len参数均来源于数据包内,为用户可控,导致攻击者可以通过设置过长长度触发memcpy越界写入,触发时的漏洞函数为处理BOOTP协议,这个协议是DHCP协议前身,DHCP兼容这个协议,在处理Bootp消息时,没有检查长度导致在复制mac时产生溢出。
参考链接
https://bbs.kanxue.com/thread-278835.htm
https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/preparing-to-debug-the-service-application#-enabling-the-debugging-of-the-initialization-code
原文始发于微信公众号(闲聊趣说):CVE-2023-38148 Internet Connection Sharing 远程代码执行漏洞分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论