科普:分析一个flash exploit的样本

作者:int3

有不少朋友一直怂恿我写这么一个马后炮分析的文章,也拖了好久,现在放出来了。

首先感谢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中恢复进程的上下文等。

相关日志

发表评论