某工业组态软件缓冲区溢出漏洞分析 – 作者:flypuma

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 组态软件的设计实现

创建完成端口:

image.png初始化socket

image.png绑定处理事件

image.png事件处理线程

image.pngGetQueuedCompletionStatus

image.png有客户端连入的时候,调用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 补丁分析

首先定位到补丁函数

image.png

通过对比,我们可以发现对应的修改为:

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

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论