样本三
如下所示在该样本中,使用了Ole10Native的流,因此没有equative head,默认读取红框中的4位长度。之后的metf head为01.
可以看到metf head的长度为01时,直接进入到if判断中(该if中的函数实际是一个异常处理函数,但是当传入的参数为45,145时不影响之后的漏洞利用),之后读取0A一位,因此当metf head的version为01时,实际的metf head的长度只有两位。
往后读取01,进入11882流程,导致直接返回,进入case流程。
进入case中的0798流程,读取08,进入之后的fun_Matrixdispatch,此时进入fun_Matrixdispatch中的11882/0802流程。
样本四
如下所示可以看到样本使用的是常用的equationative流,长度为1c的字节,之后紧跟metf头,但是咋看之下却并不能确定其具体的漏洞cve,从metf之后的88字节来看,应该是对应的11882/0802漏洞
从vt上报的漏洞类型来看,大多数厂商的识别是0802系列,由于从上文所知,metf之后的构造拥有多种方式,因此我们就来看看这个漏洞是否真是0802.
可以看到fun_ParseMTEFData函数中,metf头检验之后,向后读取一字节,此时获取的为0d,作为参数进入到正常的11882/0802流程,0d>09,因此函数直接跳过再次向下读取。
一直跳过0D 0A,直到蓝色框中的01字节。
01被正常11882/0802返回,直接dispatch到case1中。
进入case1流程中的0798,向下再读取一字节01。
0798中由01 dispatch进入到case0流程。
如上文所示,在样本二中提到进入该函数中实际上是有方法进入到对应的equation流程中,但是在当时的漏洞利用样本中,该函数直接进入到if流程中,当时的分析中提到要进入else中需要相关的特殊构造,即a4等于1,else中实际是一个while循环,依次读取并传入到0798的dispatch中。
可以看到while循环中之后会读取02的tag,在该tag中会以此处理之后的3个字节,样本中一共构造了四对02的tag用于混淆,之后处理35tag,35tag->5,即tag 5,而tag5实际上就是0798的漏洞tag,因此基本可以证明该样本使用的漏洞是0798.
如下所示,读取对应的35位tag
导致dispatch到case4中,即对应的0798漏洞流程中。
0798函数中,以此向后读取5字节的内容,其中第四,第五字节将导致溢出,即下图中的20,80,之后分别以这两字节内容为长度进行栈上数据的拷贝,一共调用两次,最终两段栈上数据连成一片,从而造成溢出。
第一次拷贝,参数为20,及0012f3fc,0012f3fc为目标栈上的地址。
第二次,拷贝,参数一为80,参数二为0012f404。可以看到第二次拷贝的地址在第一次拷贝的高地址出。
拷贝前的堆栈。
拷贝之后的堆栈,可以看到此时返回地址已经被覆盖,但是这个覆盖的数据是不是很奇怪,50505050这个地址并没有意义,难道这只是测试样本?但是从沙箱的结果来看样本是执行成功的。
仔细再看一下漏洞函数可以发现,实际上在溢出之后,函数返回之前,还有相当一部分的操作,即之后的sub_4428F0函数,及之后的还有一次0798的操作,也就是还能向后读取数据,结合对应的样本数据,可以发现确实还能向后拷贝,sub_4428F0函数之后将其结果作为参数,返回给0798漏洞流程,同时也能看到第一次溢出的拷贝的数据v6,是作为第四个参数传入到该函数中的,我们的看看这个函数具体做了啥。
首先可以看到a2,a3这两个参数来自于sub_43b349函数,值分别为word_45B246和word_45B244
而word_45B246和word_45B244的相关引用函数如下所示,其中Headcheck引起了我们的注意
可以看到这两个位置的数据是在metf头检验中进行的设置,即函数sub_43B1D0。
可以看到sub_43B1D0中设置a2,a3的值是依赖于metf头之后的一位的值,且在处理的过程中有ReadByte的操作,这就意味这实际上可以通过该函数来实现往metf头之后插入混淆数据的操作。
但是实际上仔细看代码就可以知道,上面a2,a3是取的栈上的地址,也就是说这个值实际上在溢出之后是可控的。
再次调试可以看到a2,a3的地址分别为0012f420,0012f41c。
溢出之后可以看到此时a2,a3的值被修改。
如下所示,该函数的调用栈,需要注意的是2,3,4参数,其中第四个参数为溢出数据的第一段内容,2,3参数的低四字节(溢出时被修改为5800,06eb)需要注意,下文中会有相关解释。
进入函数,可以看到首先通过sub_435B4A返回了一段内存v5,之后通过传入的参数对该段内存进行初始化,其中红框中的三处赋值初始化是关键,第一二次赋值分别将二三参数的低四位分别去处并v5偏移40和42处(06rb5800),之后通过函数memory将a4的0x14长度的数据拷贝到v5偏移50处,a4就是之前0798溢出时的第一次拷贝shellcode
此时反编译一下00281c3c这段内存,可以看到之前第一次拷贝的06eb5800的对应反编译源码为pop eax,jmp 00281c6e
而00281c6e正好就是之前v5+50处拷贝的溢出shellcode的第一段,而最终00281c3c这段内存会作为参数返回。
00281c3c的作为返回值返回,并作为参数1传入到之后的0798 dispatch函数中。
进入dispatch后,之前溢出shellcode之后的35将再次触发0798的漏洞,函数首先分别获取参数1偏移0x28,和0x2a处的数据,并保存到局部变量中,需要注意是获取的是两字节,赋值后内存中变成了00445800.
0044实际上只之前sub_4428F0函数的返回地址。
有意思的是在漏洞函数中sub_438349中,该函数会设置对应传入的a2,a3地址的值,此时正好将00445800设置为00440000(该值在之后的利用中被使用).
如下所示之后35之后依次读取的5个字节为35, 33,36,20,44(其中用于控制溢出长度的字节为20,44)。
第一次拷贝,拷贝地址为0012f35c,长度的控制参数为20.
第二次拷贝,拷贝地址为0012f376,长度的控制参数为44.
拷贝前的堆栈数据。
拷贝后的栈数据,此时返回地址已经被修改为00430bfb,需要注意的此时覆盖的只是返回地址的第四位,即高四位并没有被修改,返回地址后四位即为传入的第一个参数00281c3c,同样需要注意的还有00281c3c之后的00440000,现在来整理以下第二次漏洞利用的流程
00281c3c作为参数一传入0798漏洞函数,之后分别获取00281c3c偏移40,42处的内容),并赋值给v5,v6,其中v5就是0044000(其来源上文中已经说明),之后将00281c3c,v5,v6分别作为2,3,4参数传入到dispatch函数中。
之后进入漏洞函数2,3,4参数又分别作为1,2,3参数来调用case 4处的漏洞函数。
此时函数返回,由于之前的漏洞将函数的返回地址已经修改为00430bfb,而该地址对应的反汇编代码为ret,执行ret,将导致程序的执行流程直接进入到00430bfb之后的00281c3c中。
00281c3c向下执行,最终导致执行之后00281c3c中已经构造好的shellcode
这里可以看到在jmp前,有一条pop eax的操作,此时pop,即将之前栈上00281c3c之后的00440000赋值给eax,该值作为之后shellcode中寻址的高位地址,最终从此处进入到攻击者控制的流程中。
由此可以看到该样本中0798和正常0798的区别之处
- 首先正常进行一次0798的漏洞利用,在上布置好后续的shellcode
- 之后精心在函数sub_4428f0中构造出一段包含跳板shellcode的内存,并将该段内存作为参数再次调用0798函数。
- 再次触发漏洞,通过ret的方式将程序的执行流程定向到之前的跳板shellcode中。
总结
如下所示即可看到11882/0802/0798这三个漏洞的混淆点了,对于这三个漏洞,除了绿色1c,*8(11882,0802),*1/*4-*5(0798)不能变,其他位置都是可变的,其中紫色指有要求的变(第一处不能大于0x20000,第二处只能为01(当为01时,metf头由五位变成1位,即之前的后四位去掉),02,03,102,103),当metf为五位时,之后的一位,即第六位实际上是可以导致之后有附加数据的,及下图中的蓝色部分,因此metf头之后和tag头之间也是可以插入混淆数据的。除此之外eqnolefilehdr,mtef head其余位置应该都是可以随便搞的。
之后mtef byte stread中可以插入任意多的tag,只要保证最终进入08,05tag即可,因此在紫色01和绿色08之间也是可以加入混淆数据的(通过0798中的处理或标准流程中11882处理(>9,01,04会进入0798),0798中还依赖dispatch来处理,case0/case1/case5流程中是可以再次进入到0798,如上文的第2,4个样本。
转载请注明出处