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还原了
漏洞成因
根据漏洞描述:
- 调用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清零,这里形成了一个悬挂指针
- 调用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
- 首先我们需要创建一个channel object
- 创建两个
CKeyframeAnimationMarshaler
类型的资源对象,在DirectComposition::CApplicationChannel::ProcessCommandBufferIterator
得到CreateResource的dcmop Command值为1
通过DirectComposition::CApplicationChannel::CreateInternalResource
分析可知CKeyframeAnimationMarshaler
类型的资源对象id为0x5A
这样CreateResource
的batch buffer
参数就已经分析出来了
-
然后调用两次
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、ReleaseSource
的batch buffer
参数可以同上在DirectComposition::CApplicationChannel::ProcessCommandBufferIterator
分析得出
- 调用
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/
《微软漏洞细节披露》