作者:360威胁情报中心
公众号:360威胁情报中心
2018年8月24日,360威胁情报中心捕获到一个专门为乌克兰语使用者设计的钓鱼文档:该文档为RTF文件格式,且有详细的文档内容。360威胁情报中心经过分析确认这是首次发现的针对Office公式编辑器特殊处理逻辑而专门设计的用于绕过杀毒软件查杀的漏洞利用样本,涉及的漏洞正是CVE-2017-11882。
由于漏洞触发后的Payload已失效,360威胁情报中心在本文中专门针对样本使用的特殊免杀技术进行详细分析,并提醒各杀毒软件厂商做好针对该利用方式的检测。由于该免杀技术已经出现在在野利用的样本中,后续可能会有大量实际攻击样本使用该免杀手段逃过杀软检测,形成新的威胁。
捕获到的样本在VirusTotal上查杀效果如下,首次上传时仅有4家杀软可以查杀,且仅有两家杀软能正确识别漏洞,如果稍加改动几乎可以躲过所有杀软的查杀:
捕获到的特殊样本在RTF控制字“\objdata”的header后面居然不是一个Compound File Binary Format(复合二进制文档)流,其紧跟的直接就是公式对象的数据(MTEF data),甚至连公式对象的头部数据(Equation Native Header)都没有:
而这样一个“畸形”的CVE-2017-11882漏洞利用文档,竟然能成功触发漏洞利用。
我们先复习一下正常的RTF文档中利用Office公式编辑器漏洞的方式,以CVE-2017-11882为例:
首先,RTF文档中会被插入一个objdata,紧跟在RTF控制字“\objdata”后,随后的数据结构为4字节的版本标识、format_id(embed代表嵌入式)、OLE流名字(Equation.3)等等:
Header:
01050000 // version
02000000 // format_id (embed)
0b000000
4571756174696f6e2e3300 // "Equation.3" could be anything
0000000000000000
410f0000 // data length
在“\objdata”的header后紧跟OLE对象流,可以看到其特殊的Compound File Binary Format(复合二进制文档)标识:D0 CF 11 E0 …
紧跟的OLE对象流是一个复合二进制文件(Compound File Binary Format),通过解析可以看到这是一个Office 公式3.0编辑器对象,Root Entry带有一个公式编辑器的CLSID {0002CE02-0000-0000-C000-000000000046}:
包含的Office 公式3.0编辑器对象由公式头+公式数据组成:
360威胁情报中心针对该特殊的漏洞利用技术进行了详细分析,整个分析过程如下。
带着Office为什么可以正确处理嵌入的非OLE对象(且该对象是一个没有公式头的公式对象)这一疑问,我们详细分析了Office处理RTF文档中嵌入的\objdata对象的过程,整个处理过程可以用以下流程图表示:
从整个流程来看,当WINWORD.EXE加载RTF文件并解析RTF文件格式后会调用函数ole32!OleConvertOLESTREAMToIStorage
将指定的对象从OLE 1存储模型转换为OLE 2结构化存储对象。其内部调用的ole32!wConvertOLESTREAMTOIStorage
负责从RTF文件中解析、转换OLE 1对象到OLE 2存储对象,最后ole32! GenericObjectToIStorage
函数负责将OLE 2存储对象通过剪切板的方式传送给EquEdt32.exe进程处理:
首先ole32!wConvertOLESTREAMTOIStorage函数将具体事务交给Ole32! OLESTREAMToGenericObject:
Ole32! OLESTREAMToGenericObject
函数会完成OLE 1对象读取及转换,内部会调用OLE1StreamToUL和OLE1StmToString(内部也调用OLE1StreamToUL函数)读取OLE1对象Version、format_id、Class Name(Prog ID)、static object、linked nor an embedde、topic、Item、NativeData等信息,也就是处理\objdata的header部分:
同样可以通过oletools的rtfobj工具查看对应的\objdata得到相同信息:
进一步会判断format_id
是否为FMTID_EMBED
(linked nor embedde),然后调用wCLSIDFromOle1Class函数读取Ole1对象的CLSID:
wCLSIDFromOle1Class函数判断传入的szProgID是否是名字为“OLE2Link”的对象,如果是则返回CLSID_StdOleLink
,否则交给代理函数CLSIDFromOle1Class转换Ole1的流名字到对应的CLSID(也就是转换流名称Equation.3到对应的clsid):
CLSIDFromOle1Class交给代理函数wCLSIDFromOle1Class处理:
wCLSIDFromOle1Class函数将打开注册表HKEY_CLASSES_ROOT\[szProgID]
(此处为HKEY_CLASSES_ROOT\Equation.3)查询其CLSID,如果查询成功则调用wGUIDFromString函数得到GUID返回:
如果通过流名称查询不到CLSID则调用Ole10_CLSIDFromString
函数遍历OLE32中内置的Object名称,发现相同则返回其CLSID:
ole32! GenericObjectToIStorage函数根据返回的CLSID注册剪切板,并把OLE 2数据写入剪切板:
随后WINWORD.EXE会调用ole32!Load函数加载该CLSID对应的对象,最终定位到函数ole32!CoIsOle1Class
,该函数判断CLSID是不是有效的Ole1Class对象,不是有效的Ole1Class对象则直接返回,是有效的Ole1Class对象则调用接口处理剪切板数据,以下是加载处理公式对象的过程:
特别注意的是,WINWORD.EXE在调用ole32!OleLoad函数前,会解析CFB文件将CFB文件的流对象写入剪切板并且将Embedded对象数据块(即d0cf11e0a1b11ae10对应的块)的Clsid值覆盖之前通过ProgID获取的Clsid,也就是最终以Embedded对象数据块内的clsid为准:
最后,Office将公式对象数据传递给Equation处理:
EquEdt32.exe进程能够处理两种流,分别是Equation Native和01Ole10Native流,Equation Native是较为常见的流格式,一般以0x1C开头。而01Ole10Native流在EquEdt32.exe打开Equation Native流失败的情况下才使用:
随后会根据打开的流读取流内容, 如果是01Ole10Native流则先读取4字节流大小,如果不是则读取0x1C字节大小的Equation Native头,然后才从Equation Native头解析流01Ole10Native大小,最后分配内存用于读取01Ole10Native流数据:
可以看到,本次捕获到的免杀样本在处理过程中,由于读取Equation Native失败,所以Equation通过读取01Ole10Native流大小来直接处理后面附加的公式数据(03010103…):
随后再次调用IStream::Read(coml2!CExposedStream::Read)
函数读取流数据:
最后将流数据传入sub_42F8FF函数实现具体01Ole10Native流处理,最终成功触发漏洞:
我们回顾Office处理RTF中\objdata对象的流程可以总结出该免杀样本触发Equation漏洞的过程:
攻击者在\objdata后附带非CFB格式的数据(只有公式数据的01Ole10Native流),迫使Office通过\objdata header中的流名字(Equation.3)来查找对应处理的clsid,转入处理流程。
由于附带的是公式对象的01Ole10Native流(030101…部分数据),所以EquEdt32.exe进程打开Equation Native流失败,转而以\objdata header中指定的数据长度直接处理01Ole10Native流,触发漏洞利用。
由于该免杀样本\objdata后附带非CFB格式的数据(D0 CF 11…),而正常情况必然是携带的CFB数据,并且以CFB数据中获取的clsid为准寻找处理该对象的程序(如Equation),这直接导致绕过大部分杀软的检测逻辑。并且后续的公式数据没有公式对象头等特征,也使得部分杀软抓瞎,这是该样本绕过杀软检测的主要原因。
MD5
0a8efb742ef488bcb8ccd6b616125eea
[2].https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2017-11882