查看原文
其他

CVE-2017-0176漏洞简单分析

有毒 看雪学院 2019-09-17

信息搜集

1、漏洞文件:gpkcsp.dll

2、漏洞函数:gpkcsp!MyCPAcquireContext()

3、漏洞参数:gpkcsp!ProvCont.key

4、数据对象:一个80字节的堆缓冲区



漏洞原理分析

背景知识

Smart Card

MS-RDPEFS

MS-RDPESC


在进行后续分析之前,需要了解RDP的基础知识,否则会很困难。这部分在微软官网都有资料,我就不废话了。毕竟阅读文档也是一项技能啊,需要练。

 

如果RDP服务器开启了Smart Card登录功能,用户就可以使用Smart Card进行RDP登录。


在向用户展示登录终端之前,服务器会使用MS-RDPEFS协议建立一个Smart Card RedirectionRDP服务器检查重定向的加密服务提供程序(CSP)内的密钥容器的句柄,如果密钥容器的句柄不存在,则应用程序调用MyCPAcquireContext()函数。密钥容器是包含特定CSP的所有加密密钥的数据库。



漏洞流程


1、MyCPAcquireContext()函数首先调用SCardEstablishedContext()函数创建一个smart card上下文。


2、MyCPAcquireContext()函数调用ConnectToCard()函数使用上面的上下文创建一个连接句柄。

3、MyCPAcquireContext()函数调用SCardGetStatusChangeW()函数检查device的状态。


如果card状态为SCARD_STATE_PRESENT,MyCPAcquireContext()调用DoSCardTransmit()函数发送Transmit()_Call消息进行smart card读取,包括对应的smart card device上的序列号和不同的文件。


4、调用gpkcsp!VerifyDivPIN+0x247()函数读取安全域类型文件。gpkcsp!VerifyDivPIN+0x247()函数首先选择Master File(MF)然后发送一个SELECT命令,在DataField字段中,包含安全域文件名称((\xa0\x00\x00\x00\x18\x0f\x00\x01\x63\x00\x01)。


如果文件存在,smart card响应“\x61\xff”状态,server发送GET RESPONSE命令去读取文件。如果文件不存在,server再次发送一个SELECT命令,但是为不同的文件名称("\x47\x54\x4f\x4b\x18")。


如果文件存在,然后函数发送GET RESPONSE命令,LeField字段的值为0xff。此时,smart card通过Transmit_Return消息返回文件的内容。


5、Transmit_Return消息长度存在cbRecvLength字段,并且真实数据存储在pbRecvBuffer字段。MyCPAcquireContext()函数从cbRecvLength中减去7,并从ProvCont.key变量中的pbRecvBuffer中存储(cbRecvLength -7)个字节。

其中,ProvCont的结构如下:


struct ProvCont{
int va1_00;
HANDLE handle_04;
int var2_08, var2_0C, var3_10, var5_14;
char key[0x80];
int var6_98;
HCRYPTKEY h11;
HCRYPTKEY hc2;
...
}


其中ProvCont.key的长度是0x80,但是在存储pbRecvBuffer之前函数不检查(cbRecvLength -7)是否小于0x80。


如果攻击者可以模拟smart card device并在pbRecvBuffer中发送大于0x87的字节,则GET RESPONSE命令前面带有SELECT命令,文件名为“\xa0\x00\x00\x00\x18\x0f\x00\x01\x63\x00\x01”或”\x47\x54\x4f\x4b\x18“,然后就会发生堆缓冲区溢出情况。


综合以上分析,如果远程未经身份验证的攻击者可以通过模拟smart card device并将精心制作的smart card重定向消息发送到目标服务器来利用此漏洞将导致攻击者获得使用SYSTEM权限执行任意代码的能力。



流量侧检测防御思路


检测从RDP服务器到RDP客户端的Smart Card Redirection数据包中的Transmit_Call消息:


[Client] <---Server Announce Request--------------------[Server]
[Client] ----Client Announce Reply---------------------> [Server]
[Client] ----Client Name Request-----------------------> [Server]
[Client] <---Server Core Capability Request------------- [Server]
[Client] ----Client Core Capability Response-----------> [Server]
[Client] <---Server Client ID Confirm------------------- [Server]
[Client] ----Client Smart Card List Announce Request---> [Server]
[Client] <---Server Device Announce Response(1)--------- [Server]
[Client] <---Server Device I/O Request------------------ [Server]
[Client] ----Client Device I/O Response----------------> [Server]


上述为MS-RDPEFS协议初始化的数据包结构。


Device I/O请求和响应数据包在SmartCardRedirection期间进行交换,而且包含很多不同的类型,例如Device Create 请求和响应、Device Write 请求和响应、Device Control请求和响应等。而Transmit_Call消息封装在Device Control 请求包中。结构如下:


Offset Size(bytes) Description
------------------------------------------
0x00 4 |tpktHeader (TPKT header)
0x04 3 |x224 (Data TPDU)
0x07 n |mcsPDU
0x07+n 2 |securityHeader_flags
0x09+n 2 |securityHeader_flagsHi
0x0b+n m |securityHeader_Data (optional)
0x0b+n+m k |deviceControlData


mcsPDU是可变长度“对齐”打包编码规则(PER)编码的多点通信服务(MCS)域PDU,它封装了一个MCS Send Data Indication


1、检测设备需要具备PER(Packed Encoding Rules)解码功能,以检测mcsPDU之后的字节数据。(AF引擎不具备该功能,所以考虑其他冒险方法提取规则)。


2、检测securityHeader_flags是否被设置为SEC_ENCRYPT(0X0008)。如果该位被设置,则检测设备必须解密deviceControlData


其结构如下:


offset |Size(bytes)|Description |
|
-----------|--------|--------------------------|
|
0x00 |4 |channelPDUHeader_length |
|
0x04 |4 |channelPDUHeader_flag |
|
0x08 |2 |Component (0x4472)|
|
0x0a |2 |PacketID (0x4952) |
|
0x0c |4 |DeviceID |
|
0x10 |4 |FieldID |
|
0x14 |4 |CompletionID |
|
0x18 |4 |MajorFunction (0x0000000E)|
|
0x1C |4 |MinorFunction |
|
0x20 |4 |OutputBufferLength |
|
0x24 |4 |InputBufferLength |
|
0x28 |4 |IoControlCode (0x000900d0)|
|
0x2C |20 |padding |
|
0x30 |n |Device_InputBuffer|


检测工具需要检测Device Control Request,然后重点检测以下几个部分:


Component为RDPDR_CTYP_CORE(0x4472)、PacketId为 PAKID_CORE_DEVICE_IOREQUEST (0x4952)MajorFunction 为IRP_MJ_DEVICE_CONTROL (0x0000000E)、 IoControlCode 为SCARD_IOCTL_TRANSMIT ( 0x000900d0)


需要注意,以上数据需要按顺序检测,如果以上任何一个没有匹配上,检测设备就应该停止后续检测。


3、如果步骤3中的几个fields中的数据都能匹配上,检测设备需要导出并分析Device_InputBuffer字段中被编码的Transmit_Call消息。Transmit_Call消息使用了Type Serialization Version 1 进行编码,检测设备需要具备对Type Serialization Version 1的解码能力。


使用Type Serialization Version 1编码的包数据包含Common Type header,其结构如下:


|Offset |Size(byte) |Description|
|---------|--------|------------------|
|0x00 |1 |version (0x01) |
|0x04 |1 |Endianness |
|0x08 |2 |CommonHeaderLength (0x08)|
|0x0C |4 |Filler|


Type Serialization Version 1 编码的数据使用NDR进行序列化


4、Transmit_Call的结构:


typedef struct _Transmit_Call {
REDIR_SCARDHANDLE hCard;
SCardIO_Request ioSendPci;
[range(0,66560)] unsigned long cbSendLength;
[size_is(cbSendLength)] const byte* pbSendBuffer;
[unique] SCardIO_Request* pioRecvPci;
long fpbRecvBufferIsNULL;
unsigned long cbRecvLength;
} Transmit_Call;


cbSendLength字段定义了pbSendBuffer字段的数据长度。检测设备必须检测这两个字段的数据。cbSendLength字段的数据需要大于5,pbSendBuffer字段需要包含以下数据:


\x00\xA4\x04\x00\x0b\xa0\x00\x00\x00\x18\x0f\x00\x01\x63\x00\x01


或者


\x00\xa4\x04\x00\x05\x47\x54\x4f\x4b\x18


5、如果步骤5中的数据被检测到,则检测设备必须检查相同虚拟信道的后续设备控制请求,这要求mcsPDUSendDataRequest中的ChannelID必须相同。数据包的deviceControlData字段中的IoControlCode值必须为SCARD_IOCTL_TRANSMIT


如果找到此类数据包,则检测设备必须提取并解析Device_InputBuffer并检查Device_InputBuffer字段中的cbSendLengthpbSendBuffercbSendLength的值必须大于4,pbSendBuffer的值必须为\x00\xc0\x00\x00\xff。


6、步骤6检测成功后,需要检测对应的Device Control Reponse数据包。与request包只有mcsPDUdeviceControlData字段不同。


response包中的mcsPDU包含PER编码的MCS Send Data RequestMCS Send Data Request的结构如下:


SendDataRequest ::= [APPLICATION 25] IMPLICIT SEQUENCE {
initiator UserId, // INTEGER(1001..65535)
channelId ChannelId, // INTEGER(0..65535)
dataPriority DataPriority, // ENUMERATED {0,1,2,3}
segmentation Segmentation, // BIT STRING {0,1}(SIZE (2))
userData OCTET STRING
}


deviceControlData的结构如下:


|offset |Size(bytes) |Description |
|---------|----------|-------------------|
|0x00 |4 |channelPDUHeader_length|
|0x04 |4 |channelPDUHeader_flag |
|0x08 |2 |Component (0x4472)|
|0x0a |2 |PacketID (0x4943) |
|0x0c |4 |DeviceID |
|0x10 |4 |CompletionID|
|0x14 |4 |IoStatus |
|0x18 |4 |OutputBufferLength |
|0x1C |4 |Device_OutputBuffer |


检测设备需要检查ComponentPacketId字段的值是否为RDPDR_CTYP_CORE(0x4472)PAKID_CORE_DEVICE_IOCOMPLETION(0x4943)。


如果字段值匹配,需要解析Device_OutputBuffer字段,该字段包含Type Serialization Version 1编码的Transmit_Return消息,其结构如下:


typedef struct _Transmit_Return {
long ReturnCode;
[unique] SCardIO_Request *pioRecvPci;
[range(0, 66560)] unsigned long cbRecvLength;
[unique] [size_is(cbRecvLength)] byte *pbRecvBuffer;
}Transmit_Return;


ReturnCode字段值为0(success)或者1。cbRecvLength为pbRecvBuffer字段的数据的长度。


检测设备必须检测ReturnCode是否为0和cbRecvLength是否大于0x87,如果两个都匹配,则可以怀疑存在恶意攻击流量。




- End -



看雪ID:有毒 

https://bbs.pediy.com/user-779730.htm  



本文由看雪论坛 有毒 原创

转载请注明来自看雪社区





往期热门回顾

1、C/C++反混淆方法

2、APICloud解密本地资源到逆向APP算法到通用资源解密

3、我的微信数据监控研究发展过程

4、网络沙场大点兵!2019 WCTF世界黑客大师赛鸣锣开战

5、如何实现 Https拦截进行 非常规“抓包”








京华结交尽奇士,意气相期矜豪纵。今夏与君相约看雪,把酒言欢,共话安全技术江湖梦。


10大议题正式公布!第三届看雪安全开发者峰会重磅来袭!




      ↙点击下方“阅读原文”,查看更多干货

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存