Windows RDP的RCE漏洞分析和复现(CVE-2019-0708)(4)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 图14.刷新Rect PDU数据 numberOfAreas字段是一个8位无符号整数,用于定义areasToRefresh字段中的包含Rectangle结构的数量。areaToRefresh字段是TS_RECTANGLE16结构的数组,如图15所示。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?图15.包含Rectangle(TS_RECTANGLE16) ? Refresh Rect PDU通过一系列“Inclusive Rectangles”操作通知服务器,以使服务器重新分配一个会话。基于默认通道,通道ID为0x03ea(服务器通道ID)。连接序列完成后,如图1所示,RDP服务器可以接收/解析刷新矩阵PDU,最重要的是,可以合法地多次发送。虽然对于TS_RECTANGLE16结构仅限于8个字节,意味着RDP客户端只能控制8个字节,但仍然可以将任意数据写入内核。 4.使用Refresh Rect PDU将数据写入内核正常解密的Refresh Rect PDU如图16所示。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 图16.解密的Refresh Rect PDU 内核模块RDPWD.sys代码函数WDW_InvalidateRect负责解析Refresh Rect PDU,如下面的图17所示。 ? ? ? ? ? ? ? ? ? ? ?图17. RDPWD!WDW_InvalidateRect堆栈跟踪 如图18所示,WDW_InvalidateRect函数将解析Refresh Rect PDU流并从流中检索numberOfAreas字段作为循环计数。作为字节类型字段,numberOfAreas的最大值为0xFF,因此最大循环计数为0xFF。在循环中,WDW_InvalidateRect函数将获得TS_RECTANGLE16结构中的左,上,右和下字段,将它们放在堆栈中的结构中,并将其作为WDICART_IcaChannelInput的第 5 个参数。这里要提到的是,WDICART_IcaChannelInput的第6 个参数是常数0x808,我们将展示它如何有效地实现堆喷。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 图18. RDPWD!WDW_InvalidateRect函数 WDICART_IcaChannelInput最终将调用内核模块termdd.sys函数IcaChannelInputInternal。如图19所示,如果一系列条件检查为True,则函数IcaChannelInputInternal将调用ExAllocatePoolWithTag来分配inputSize_6th_para + 0x20大小的内核池。因此,当函数IcaChannelInputInternal由 RDPWD!WDW_InvalidateRect,inputSize_6th_para = 0x808调用时,内核池的大小为0x828。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 图19. termdd!IcaChannelInputInternal ExAllocatePoolWithTag和memcpy 如果内核池分配成功,将调用memcpy将input_buffer_2复制到新分配的内核池内存。图20显示了当调用者是RDPWD!WDW_InvalidateRect时memcpy的参数。 ? ? ? ? ? ? ? ? ? ? ? ? 图20. termdd!IcaChannelInputInternal memcpy windbg转储 (编辑:ASP站长网) |