0×00 漏洞信息
CNVD-ID:CNVD-2020-43553
某公司工业组态软件存在缓冲区溢出漏洞,攻击者可利用该漏洞执行任意代码。
补丁信息:http://www.kingview.com/news_info.php?num=1001753
漏洞发布:http://www.cnvd.org.cn/flaw/show/CNVD-2020-43553
0x01 完成端口
该程序采用C/S模式进行网络通信,采用Windows完成端口设计实现异步通信。完成端口会充分利用Windows内核来进行I/O的调度,是用于C/S通信模式中性能最好的网络通信模型。
1.1 完成端口原理
进行异步通信时,事先开好几个线程,有几个CPU就开几个。这样首先避免线程的上下文切换,因为线程想要执行的时候,总有CPU资源可用。让这几个线程等着,等到有用户请求来到的时候,就把这些请求都加入到一个公共消息队列中去,然后这几个开好的线程就排队逐一去从消息队列中取出消息并加以处理,这种方式优雅的实现了异步通信和负载均衡的问题。它提供了一种机制来使用几个线程“公平的”处理来自于多个客户端的输入/输出,并且线程如果没事干的时候也会被系统挂起,不会占用CPU周期。这个关键的作为交换的消息队列,就是完成端口。
1.2 使用完成端口的基本流程
使用完成端口包括以下几个步骤:
(1) 调用CreateIoCompletionPort() 函数创建一个完成端口,而且在一般情况下,我们需要且只需要建立这一个完成端口。
(2) 根据系统中有多少个处理器,就建立多少个Worker线程,这几个线程是专门用来和客户端进行通信。
(3)接收连入的Socket连接了,这里有两种实现方式:一是和别的编程模型一样,还需要启动一个独立的线程,专门用来accept客户端的连接请求;二是用性能更高更好的异步AcceptEx()请求。
(4)每当有客户端连入的时候,调用CreateIoCompletionPort()函数,是把新连入的Socket与目前的完成端口绑定在一起。至此,实就已经完成了完成端口的相关部署。
(5) 客户端连入之后,我们可以在这个Socket上提交一个网络请求,例如WSARecv(),然后系统就会执行接收数据的操作。
(6) 而此时,预先准备的Worker线程都需要分别调用GetQueuedCompletionStatus() 函数在扫描完成端口的队列里是否有网络通信的请求存在(例如读取数据,发送数据等),一旦有的话,就将这个请求从完成端口的队列中取回来,继续执行本线程中后面的处理代码,处理完毕之后,我们再继续投递下一个网络通信的请求如此循环。
1.3 组态软件的设计实现
创建完成端口:
初始化socket
绑定处理事件
事件处理线程
GetQueuedCompletionStatus
有客户端连入的时候,调用CreateIoCompletionPort()函数,当端口接收完数据,调用sub_10006DC0(v2,CompletionKey,NumberOfBytesTransferred)处理接收到的数据包,程序的漏洞也出现在这里。
0x02 漏洞原理
mov edx, [ebp+var_64]
mov [ebp+MaxCount], edx
mov [ebp+var_64], 0
push 4Ch ; int
lea eax, [ebp+var_6C]
push eax ; int
lea ecx, [ebp+var_14]
call unknown_libname_18 ; Microsoft VisualC2-11/net runtime
mov ecx, [eax+4]
mov edx, [ecx+4]
push edx ; s
push 1 ; int
mov ecx, [ebp+var_954]
call sub_10006430
xor eax, eax
mov [ebp+Dst], ax
push 7FEh ; size_t
push 0 ; int
lea ecx, [ebp+var_872]
push ecx ; void *
call memset
add esp, 0Ch
mov edx, [ebp+MaxCount]
push edx ; MaxCount
lea ecx, [ebp+var_14]
call unknown_libname_18 ; Microsoft VisualC2-11/net runtime
mov eax, [eax+4]
add eax, 0A4h
push eax ; Src
push 800h ; DstSize
lea ecx, [ebp+Dst]
push ecx ; Dst
call ds:memcpy_s
add esp, 10h
mov [ebp+var_898], eax
lea edx, [ebp+Dst]
push edx
lea ecx, [ebp+var_894]
call std::allocator<wchar_t>>(wchar_tconst *)
mov byte ptr [ebp+var_4], 1
lea eax, [ebp+var_894]
push eax
lea ecx, [ebp+var_74]
push ecx
mov ecx, [ebp+var_954]
add ecx, 20h
函数调用memcpy_s (
void *dest,
size_t numberOfElements,
const void *src,
size_tcount
)
第四个参数count来源于输入数据包的第三个DWORD,它表示数据包的大小。当该数据太大时,产生溢出,该溢出可导致任意代码执行。
0x03 补丁分析
首先定位到补丁函数
通过对比,我们可以发现对应的修改为:
MaxCount =v64; //PacketHeader.DataLen
v64 = 0;
v8 =operaror__((int)v49);
SendPacket(buf,0x4C, *(_DWORD *)(*(_DWORD *)(v8 + 4) + 4));// SendPacket(buf,size,socket)
Dst = 0; // FlagBuf[0] = 0;
memset(&v70,0, 0x7FEu);
if ( MaxCount >= 0x400 )
{
v71 = -1;
NkxLatchLock::XDBAutoLatch::~XDBAutoLatch((NkxLatchLock::XDBAutoLatch*)&v56);
return 536903682;
}
v10 = MaxCount;
v11 =operaror__((int)v49); //->
MaxCount = memcpy_s(&Dst, 0x400u, (constvoid *)(*(_DWORD *)(v11 + 4) + 0xA4), v10);
补丁程序在调用memcpy_s()前会判断数据包长度是否会大于0x400,若大于0x400则直接退出函数。
0x04 安全建议
该漏洞允许远程代码任意执行,漏洞危险系数很高。且该程序广泛应用于工行行业,对相关工业生产造成安全隐患。用户可去公司官方网站下载补丁文件,产品供应商主动推送更新。若因生产等原因无法安装补丁的,可增加防火墙对应包过滤规则。
来源:freebuf.com 2020-07-20 14:57:48 by: flypuma
请登录后发表评论
注册