Windows Composition初探 --- CVE-2020-1135

CVE-2020-1135

前言

​ 拿到这个漏洞的漏洞描述后,发现位于Windows Composition API中,此前对Composition 没有什么了解,尽管在内核中的这个漏洞的成因很简单,但是如何在环三进行触发却是毫无头绪。好在google一番后发现了cansecwest2017上的议题《Win32k Dark Composition--Attacking the Shadow Part of Graphic Subsystem》中有这个组件的相关内容:

《Win32k Dark Composition 》

https://cansecwest.com/slides/2017/CSW2017_PengQiu-ShefangZhong_win32k_dark_composition.pdf

《Windows Composition API 指南 - 认识 Composition API》

https://void2.dev/windows-composition-api-guide-introduction/

​ 这里把ppt中对触发这次漏洞的比较关键的点简单列举一下:

  • Composition自win8后开始引入,基于dwm(desktop windows manager)工作

  • 自win10 RS1之后,内核中实现被改变,大量函数被重写,移除了大量的环三接口,并增加了一个NtDCompositionProcessChannelBatchBuffer函数,RS1之前被移除大量环三接口通过这个函数进行分发

  • 可以使用NtDCompositionCreateChannel创建一个类似于Device Object的句柄,可以通过它创建resource object,其第三个参数返回一个batch buffer环三的映射地址

  • resource object在用户层被称为视觉树中的基本对象,其包含了大量的类型

  • batch buffer:

    • 一个batch buffer与一个channel关联
    • NtDCompositionProcessChannelBatchBuffer中返回
    • NtDCompositionProcessChannelBatchBuffer负责解析其中的数据
    • NtDCompositionProcessChannelBatchBuffer支持各种各样的命令来调用其他接口,因此batch buffer中每个接口需要填写的参数也互不相同

    有了以上基础之后,就可以尝试根据微软的漏洞描述和崩溃堆栈进行POC还原了

漏洞成因

​ 根据漏洞描述:

  1. 调用ResourceSetReferenceArrayProperty dcmop命令两次:
  • 第一次CKeyframeAnimationMarshaler::SetReferenceArrayProperty将会把ppPropertyValue指针赋值给CKeyframeAnimationMarshaler->m_ppNestedExpressionList
  • 第二次调用时由于CKeyframeAnimationMarshaler->m_ppNestedExpressionList不为0,将会把status设置为STATUS_INVALID_PARAMETER,却仍然会设置ppPropertyValue指针,当CKeyframeAnimationMarshaler::SetReferenceArrayProperty返回时,由于status小于0,将会释放ppPropertyValue指向的内存池,却没有将CKeyframeAnimationMarshaler->m_ppNestedExpressionList清零,这里形成了一个悬挂指针
  1. 调用ChannelReleaseResource dcmop命令:
  • 在ChannelReleaseResource 中将会再次释放CKeyframeAnimationMarshaler->m_ppNestedExpressionList,造成double free

​ 可以看到下图代码如微软的漏洞描述所说,在把status设置为STATUS_INVALID_PARAMETER,却仍然会设置ppPropertyValue指针

DirectComposition::CKeyframeAnimationMarshaler::SetReferenceArrayProperty

​ 我们在IDA中通过交叉引用对DirectComposition::CKeyframeAnimationMarshaler::SetReferenceArrayProperty进行向上回溯时没有发现函数调用,然后在windbg中对其下断点断下后发现是由DirectComposition::CApplicationChannel::SetResourceReferenceArrayProperty调用,采用的是C++中多态的实现,因此这里就需要我们创建对应的CKeyframeAnimationMarshaler resource object,才会调用到 DirectComposition::CKeyframeAnimationMarshaler::SetReferenceArrayProperty,同时可以看到在调用完后由于status小于0,调用Win32FreePool释放了ppPropertyValue却没用清空CKeyframeAnimationMarshaler->m_ppNestedExpressionList

还原POC

  1. 首先我们需要创建一个channel object

  1. 创建两个CKeyframeAnimationMarshaler 类型的资源对象,在DirectComposition::CApplicationChannel::ProcessCommandBufferIterator得到CreateResource的dcmop Command值为1

​ 通过DirectComposition::CApplicationChannel::CreateInternalResource分析可知CKeyframeAnimationMarshaler 类型的资源对象id为0x5A

​ 这样CreateResourcebatch buffer参数就已经分析出来了

  1. 然后调用两次ResourceSetReferenceArrayProperty,这里要注意传递ArrayProperty的值要为2,这也是为什么我们要创建两个资源对象而不是一个,因为在最后ReleaseResource时会检查资源对象的引用计数是否为0,否则不会释放CKeyframeAnimationMarshaler->m_ppNestedExpressionList,而跟踪增加引用计数处的代码可以发现们可以控制给哪一个资源对象增加引用计数

    struct Resource
    {
    	int   referenceCount; // +0x14
    	void* m_ppNestedExpressionList // +0xb0
    	int   m_nestedExpressionCount // +0xb8
    }
    
    
    
    
    struct Channel
    {
    	void*	resource_table; // +0x38
    	__int64 resource_num;   // +0x50
    	__int64 sizeof_pointer;   // +0x58
    }
    

因此这里选择给资源1进行两次SetResourceReferenceArrayProperty,控制参数使得SetResourceReferenceArrayProperty时增加的引用计数是资源2而不是资源1,从而使得资源1的引用计数为1,在调用ReleaseSource时触发重复释放CKeyframeAnimationMarshaler->m_ppNestedExpressionList,至于SetResourceReferenceArrayProperty、ReleaseSourcebatch buffer参数可以同上在DirectComposition::CApplicationChannel::ProcessCommandBufferIterator分析得出

  1. 调用ReleaseResource触发double free

实验效果

实验环境: win10 1903 x64 18362.239

参考

https://cansecwest.com/slides/2017/CSW2017_PengQiu-ShefangZhong_win32k_dark_composition.pdf

https://github.com/progmboy/cansecwest2017/

《微软漏洞细节披露》

原文地址:https://www.cnblogs.com/DreamoneOnly/p/13256172.html