吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 9324|回复: 5
收起左侧

[漏洞分析] CVE-2017-0176漏洞简单分析

[复制链接]
yaoyao7 发表于 2019-6-25 17:55
本帖最后由 yaoyao7 于 2019-6-26 08:30 编辑

CVE-2017-0176原理简单分析

  • 最近有朋友问到了这个漏洞的相关信息,简单分析了一下,感觉还挺有意思的,跟各位师傅分享一下自己的想法。本文不做深入分析调试,只是简单地说一下原理和漏洞触发的过程,具体的调试希望诸位能亲自动手实践一下,收益颇多。

信息搜集

  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 Redirection。RDP服务器检查重定向的加密服务提供程序(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权限执行任意代码的能力。

流量检测防御思路:

  1. 检测从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.

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

  3. 检测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 is PAKID_CORE_DEVICE_IOREQUEST (0x4952)MajorFunction is IRP_MJ_DEVICE_CONTROL (0x0000000E)、 IoControlCode is SCARD_IOCTL_TRANSMIT ( 0x000900d0)
    需要注意,以上数据需要按顺序检测,如果以上任何一个没有匹配上,检测设备就应该停止后续检测。

  4. 如果步骤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进行序列化。
  5. 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
  6. 如果步骤5中的数据被检测到,则检测设备必须检查相同虚拟信道的后续设备控制请求,这要求mcsPDU的SendDataRequest中的ChannelID必须相同。数据包的deviceControlData字段中的IoControlCode值必须为SCARD_IOCTL_TRANSMIT。 如果找到此类数据包,则检测设备必须提取并解析Device_InputBuffer并检查Device_InputBuffer字段中的cbSendLength和pbSendBuffer。 cbSendLength的值必须大于4,pbSendBuffer的值必须为\x00\xc0\x00\x00\xff。

  7. 步骤6检测成功后,需要检测对应的Device Control Reponse数据包。与request包只有mcsPDU和deviceControlData字段不同。response包中的mcsPDU包含PER编码的MCS Send Data Request。MCS 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

    检测设备需要检查Component和PacketId字段的值是否为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,如果两个都匹配,则可以怀疑存在恶意流量。

免费评分

参与人数 2威望 +1 吾爱币 +10 热心值 +1 收起 理由
willJ + 1 + 9 优秀啊,期待楼主下一篇文章
stunyeah + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

FleTime 发表于 2019-6-25 18:24
不愧是大神,完全看不懂。。。
冰点唯银 发表于 2019-6-26 01:34
西元世纪爱 发表于 2019-6-26 10:41
ESSID 发表于 2020-11-23 11:14
学习一下
二娃 发表于 2021-10-28 17:25
感谢楼主分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 13:27

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表