首页
社区
课程
招聘
[原创]CVE-2018-8550分析与复现
发表于: 2019-5-1 21:34 8564

[原创]CVE-2018-8550分析与复现

王cb 活跃值
11
2019-5-1 21:34
8564

原poc作者James Forshaw使用C#实现,我一直未复现成功,不过通过原poc的代码我大致明白了漏洞的成因和触发方法,原poc环境是win10 1803 X64系统.在cve2018-8550更新补丁出来不久,微软就取消了64位ole32.dll和coml2.dll调试符号提供,不过32的仍然可以正常提供.为了方便调试我用vc在32位系统上成功复现了poc.
poc采用的反射从低权限进程(poc进程)向高权限进程(bits服务)Unmarshal方式实现触发bits复制了一个poc进程中的一个句柄至高权限进程从而用来创建进程实现提权,Unmarshal采用的ClassID为CLSID_DfMarshal={ 0x0000030B, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, } ,Bits服务器调用了ole32(在win10中被代码迁移到coml2)的DfUnMarshalInterface方法来Unmarshal的数据流中SDfMarshalPacket被poc精心构造的数据包,由于poc使用的是StgCreateDocfile创建IStorage对象,所以最终DfUnMarshalInterface调用了CExposedDocFile::Unmarshal来Unmarshal,下面是逆向的代码:

DfUnMarshalInterface默认是采用MSHCTX=MSHCTX_INPROC也就是同进程内Unmrahsl方式读取的指针都是的同进程对象,不过它也支持UnmarshalSharedMemory方式,也就是从结构的hMem字段从audiodg.exe的共享内存初始化相应的共享对象

windows音频服务允许建立一个Initialize在audiodg.exe进程共享内存中AUDCLNT_SHAREMODE=AUDCLNT_SHAREMODE_SHARED的共享内存,并开辟这样一块共享内存给poc进程和bits进程使用.通过ntdll未公开的函数实现找到位于audiodg.exe进程共享内存的句柄,写入交换数据后,将共享内存的句柄值写入SDfMarshalPacket->hMem,关于ntdll未公开的函数具体实现poc中有代码请读者自行研究.

在CSharedMemoryBlock::InitUnMarshal中CSharedMemoryBlockObj->_pbBase被赋值成了SDfMarshalPacket->hMem共享内存映射后的地址,之后在CSmAllocator::Init中 CPerContext->_pbBase = CSharedMemoryBlockObj->_pbBase + 8,之后在UnmarshalSharedMemory接着就调用CPerContext::SetThreadAllocatorState接着调用CSmAllocator::SetState将NtCurrentTeb()->ReservedForOle赋值成CPerContext->_pbBase.最终得出的结论是NtCurrentTeb()->ReservedForOle-8就是共享内存内存映射的相对地址.在poc中使用如下方式操作共享内存
在poc中映射的共享内存基址directoryMappedAddressLocal就是bits进程中NtCurrentTeb()->ReservedForOle-8即bits进程映射的共享内存基址.以下是poc操作共享内存的方法
CBasedMapSectionPtr pdf = (CBasedMapSectionPtr)(pck->CBasedPubDocFileObj + (ULONG)directoryMappedAddressLocal + 8);
pdf->_SelftobjectPtr = 0x46444250;

在windbg验证结论,共享内存CPerContext的地址为0617a350,可见它继承与CSmAllocator,它的CPerContext->CSharedMemoryBlockObj(+0x4)也就是CSharedMemoryBlock地址为060def40,CSharedMemoryBlock->_pbBase地址为03e40000,同时可以看到ReservedForOle指针指向的地址为03e40008,验证了结论.

设置好共享内存后会进入UnmarshalContext,其中先从SDfMarshalPacket->CBasedGlobalContextObj中通过CContextList::_Find找第一个CGlobalContext,其实CGlobalContext继承于CContextList结构,会根据CContextList->pctxNext找下一个CContextList直到CContextList->ctxid为要找的进程id为止.之后会验证下CPerContext句柄是否有效,其实只要构造一个名为"\Sessions\0\BaseNamedObjects\OleDfRoot%X%08lX"的内核Event通过NtCreateEvent,Bits服务的Sessions为0,详见poc代码.

之后进入 CFileStream::Unmarshal,同样通过CContextList::_Find和方式通过设置和bits服务相同的pid从SDfMarshalPacket->CBasedGlobalFileStreamBaseObj找第一个CFileStream也就是poc中往共享内存0x7279 - 0x10写入的CFileStream,它的CFileStream->_hFile被赋值为0导致GetFileType返回-1导致验证失败(不影响程序运行),这个时候它的ctxid已经可以检测到为0,这时会进入poc中新建线程的第一个if语句断点.由于验证失败进入else,其中fstmOut->_CGlobalFileStreamPtr被赋值成SDfMarshalPacket->CBasedGlobalFileStreamBaseObj,fstmGlobal赋值成SDfMarshalPacket->CGlobalFileStreamPtr,之后它的fstmGlobal->_CGlobalFileStreamPtr->_pctxHead=0x7279被 CContextList::Add设置成fstmGlobal的相对地址,可以去判断读取SDfMarshalPacket->CBasedGlobalFileStreamBaseObj->_pctxHead已经不是0x7279,其实是fstmGlobal的相对地址.笔者发现一个小tips,fstmGlobal->_CGlobalFileStreamPtr也是可以在SDfMarshalPacket中读取到的,这样实际上求fstmGlobal->_CGlobalFileStreamPtr-SDfMarshalPacket->CBasedGlobalFileStreamBaseObj值就可以计算出bits进程的映射共享内存的映射基址也就是(NtCurrentTeb()->ReservedForOle->pvThreadBase-8)的值,从而实现读写bits进程的任意指定真实地址(非通过偏移内存计算)内存数据在共享内存区域,有兴趣的读者可以自行尝试后续研究.

之后进入CFileStream::InitWorker,that->_CGlobalFileStreamPtr->_pctxHead=0x7279,(CFileStream*)(0x7279 - 0x10)是第一个找到的fstmFoundNew,由于的我在poc中预先设置了
fstmFoundNew->baseContext.pctxNext = 0x7279这样pctxNext又链接到了它自己,这样可以构建 while ( fstmFoundNew )无限循环,产生一个时间差,让poc中的新建线程有时间读取最终复制的句柄.

最终bits服务调用DuplicateHandle函数复制了poc中pWorkFileStream->_hFile设置的句柄,使用这个句柄后作为创建新进程的父句柄,最后成功弹出了一个System权限NotePad,如图:
查看大图

引用

我的poc地址

原poc

P0地址

作者来自ZheJiang Guoli Security Technology

 
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2019-8-9 09:02 被王cb编辑 ,原因:
收藏
免费 7
支持
分享
最新回复 (2)
雪    币: 11695
活跃值: (7179)
能力值: ( LV13,RANK:550 )
在线值:
发帖
回帖
粉丝
2
poc在https://gitee.com/cbwang505/CVE2018-8550Poc
2019-5-1 21:34
0
雪    币: 300
活跃值: (2477)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
mark
2019-5-3 22:41
0
游客
登录 | 注册 方可回帖
返回
//