百度空间 | 百度首页 
               
 
查看文章
 
科普:分析一个flash exploit的样本
2008-06-20 13:21
有不少朋友一直怂恿我写这么一个马后炮分析的文章,也拖了好久,现在放出来了。


首先感谢Mark Dowd发现这个漏洞并且分享了一些关于这个漏洞通用利用的方法。

再阅读这篇blog之前,请熟读Mark Dowd关于Flash exploit的whitepaper和Flash 9文件格式以及Adobe关于AVM2的详细文档。先简单的回顾一下flash文件格式,一个SWF文件是由SWF header和若干tag来组成的。

SWF Header Format

Mark Dowd提到的一些SWF结构用于编写这个exploit:
    DefineSceneAndFrameLabelData    86
    DefineBits                          6
    DOABC                                82
    SHOWFRAME                           1

其中SWF Tag format有2种类型:
Short Type (2 bytes)
   Upper 10 bits: tag type Lower 6 bits: tag length
Long Type (6 bytes)
   Tag type and length of 0x3F + 4 bytes Length

在这个例子中是采用的是Short类型的Tag format,所以我们需要decode一下Tag然后获取正确的Tag type,decode以后我们可以发现这些Tags对应的偏移位置。
DefineSceneAndFrameLabelData位于文件偏移的0x41e处。

值得一提的是SWF文件格式中的整数类型是经过Encoded,根据LibMing中提到的算法:
    #define ENC_HIGH_BIT 0x80
    #define ENC_BYTE_MASK 0x7f

    unsigned char temp = i & ENC_BYTE_MASK;
    i = i >> 7;
    if(i > 0)
        temp |= ENC_HIGH_BIT;

解码算法:
   #define ENC_BITSPERBYTE     7
   #define ENC_BYTEMASK         0x7f
   #define ENC_HIGHBIT        0x80
    do
    {
        if(shift > 4 * ENC_BITSPERBYTE)
            break;
       
        temp = readUInt8(f);
        result |= (ENC_BYTEMASK & temp) << shift;
        shift += ENC_BITSPERBYTE;
    } while (hasNextByte(temp))
;
文件中的99 be 8e a0 08 就是一个encoded的整数值,decode以后获得的值为:
0x08a08eb4是经过计算后的Opcode Mark的位置,根据Mark Dowd提到的算法:
    address = AS3_argmask_address + (marker_byte – (marker_byte % 4)) + 4
    scene_count = (0x80000000 | (address / 12))

              
再次还原后得到的值为0x302b3928 ,对应flash.ocx中Marker值0xf8的索引位置。
DoABC位于文件偏移的 0x463处,其中修改过的ABC(Action Byte Code)在0x5b3处。

这段ABC符合Mark Dowd的whitepaper中的描述:
    F8 62 79                        get_local
    F8 62 75                        get_local
    F8 E8 25 FB FF FF 00          pushshort
    F8 29 02                        pop nop
    F8 63 79                        set_local
    02 ... 47                       nop ... returnvoid


为了能够让大家更好的理解这个漏洞,这里简单的描述一下这个漏洞利用原理:
Flash在处理DefineSceneAndFrameLabelData结构时候存在一个空指针引用的漏洞,malloc()分配内存失败返回一个空指针,紧接着又会往这个指针+offset的地方写上任意的2字节的值,offset和写入的值都是可控,Mark Dowd建议大家覆盖AVM2中某个未使用指令的参数长度值,在这个例子中,这个未使用的指令是0xf8,我们可以把这个指令参数的长度覆盖成一个正整数值,这样AVM2的校验部分,就会跳过这个长度来检测下一条指令,如果我们在每条指令前面都加上这个marker值(如上图所示意)就可以传递一串任意的指令来达到修改栈上数据的目的。
在这里可能很多朋友会产生疑问,不用0xf8不一样能传递这些指令么,的确是能够传递,但是AVM2首先会调用alloca_probe在stack上分配一个空间来作为AVM2的虚拟栈,也就是说所有的指令都在这个栈上进行处理从而不会干扰程序原本的栈,例如get_local指令,这个指令只有一个offset的参数,而AVM2是这样解析这条指令,从虚拟栈的基地址 + offset * 4的地方取出一个dword值。如果这个offset所指向的地址,超过了虚拟栈的栈顶,AVM2的校验部分就会检测出这个行为并且阻止,但是Mark Dowd的这个方法就能够绕过AVM2的校验部分,直接让AVM2的解析部分来解析这段指令,这样就能达到修改虚拟栈外数据的目的了。

SHOWFRAME位于文件的偏移0x603处,此结构的主要用途是让flash立即处理DoABC结构。
DefineBits 的偏移在0x1c处,其中shellcode的偏移在0xea,整段shellcode采用双字节xor加密,xor key是0x4e7a,其中下载木马的地址为:

这段shellcode比较复杂并且夹杂了一些绕过HIPS的方法,例如Faking Stack Frames:

Hook NtCreateProcess/NtWriteVirtualMemory 绕过第三方保护软件针对这2个API的保护,修复CreateProcessInternalW函数的前面几个字节,也是针对一些保护软件所做的措施。


最后执行真正的功能,调用GetTempPathA()获取临时文件目录,调用DeleteFile删除orz.exe,并且下载新的木马文件保存为orz.exe然后调用CreateProcessInternalA执行它,shellcode这部分已经有朋友发了详细的分析文章,大家可以参靠的那篇。

还有一些通用性和稳定性的在这个exploit未被考虑的,比如flash的CWS功能,以及在shellcode中恢复进程的上下文等。

类别:网络安全 | 添加到搜藏 | 浏览() | 评论 (11)
 
最近读者:
 
网友评论:
1
2008-06-20 14:07 | 回复
拖了那么久终于出现了
 
2
2008-06-20 15:28 | 回复
这么久了,终于有人分析全了. avm指令里第一个setlocal保存了返回地址 而且这个shellcode恢复了上下文啊,CreateProcessInternalA后面的指令 push dword ptr [ecx-14h], dm难道没有注意? CWS就是zip了下,用来过一些弱智av.
 
3
2008-06-20 18:07 | 回复
学习
 
5
2008-06-20 23:37 | 回复
2楼的神仙做的?
 
6
2008-06-26 09:22 | 回复
学习
 
7
2008-07-03 10:43 | 回复
其实,这个漏洞讨论到最后,也就剩下两个问题: 1 不管是bytecode 里的 getlocal还是setlocal,它们用的AVM的localregister 的index(如79\75H等)怎么样分析出来的,尤其是国内第一个构造出样本的兄弟,真想和他交流下.当然,看了样本之后,再来仔细分析,就可以随意构造摸板了. 2 由于漏洞数据特征明显,即使用zlib压缩之后也难以逃多AV的检测,以后压缩(或者勉强说是加密)过程不是随机的.怎么样构造出摸板,使得不被AVAST等AV杀,这个也是个问题.
 
8
2008-07-03 17:41 | 回复
楼上的朋友,我这里只回答你第一个问题: 其实你如果熟读了那2篇pdf,答案就一目了然了,这个漏洞的利用原理其实是利用AVM指令用shellcode地址改写了当前函数的返回地址,通过调试器获取保存函数返回地址的栈空间上的地址减去alloca_probe分配的虚拟栈地址,得到值除以4就是register的index值,在这个样本里面是0x79,好像flash9e.ocx for firefox 值稍有区别以外,其他好像都是一样的,同理第二条get_local获取的是shellcode的存放的地址,这个只要你找到AVM中执行指令的部分,就是那个很大的一个swich/case的代码块,就能够很轻易的定位了。最后一个set_local就是把当前栈上的值,也就是shellcode地址写入了原来存放函数返回地址的地方,当函数从栈上返回后,就获得了程序的控制流程了,刚好shellcode指向的是byte code中的 eb xx 这个短跳转代码。
 
9
2008-07-03 17:44 | 回复
应该 F8 E8 25 FB FF FF 00 这个地方,0xf8又刚好是有效的intel指令.
 
10
2008-07-18 22:28 | 回复
感谢dm,这个问题我后来也搞清楚了。其实保存函数返回地址的地方通过调试是能得到的,就是alloc_probe的地址我之前一直没确定。
 
11
2008-07-20 19:03 | 回复
再次感谢DM兄! 彻底明白了,我之前的理解存在一个很大的问题,谢谢.
 
12
2008-11-16 01:14 | 回复
 
发表评论:
姓 名:
网址或邮箱: (选填)
内 容:
验证码: 请点击后输入四位验证码,字母不区分大小写
      

     

©2009 Baidu