• CVE-2012-0150 补丁对比分析

    日期:2012-02-25 | 分类:软件漏洞

    CVE-2012-0150是Windows C运行库msvcrt.dll上存在的一个堆溢出漏洞,微软已在2月份发布补丁。如果直接借助bindiff进行补丁比较,还是很容易定位漏洞函数,通常漏洞函数都是在相似度为0.7 ~ 0.9的地方,而且大多有G标志,也就是说图表结构存在变化:


    基本上可以断定是在前4个函数,如果你打开看的话还会发现,它们的指令内容都差不多,这就更好定位漏洞,而且里面还有memcpy这个敏感函数。下面是对比的情况,我直接通过F5来比较,显得更直观一些:


    左右两边的差别在于SizeTMult函数的第1个参数,漏洞函数多乘以2,而乘2后的值指向是memcpy中的size参数,虽然size改变了,但calloc分配的内存块并没有跟着扩大,导致分配了X大小的堆块,却可memcpy大小为2X的数据到堆块(由于漏洞函数会被循环调用,为触发漏洞创建了条件),造成堆溢出!!!

    ‍函数调用流程图:

     其实它就是在格式化浮点字符串时造成的溢出,比如1.000...000这样的超长字符串,然后在重分配缓冲区时错误地计算了缓冲区大小。我们往上追溯到winput_s函数,发现里面有多处循环调用到漏洞函数,这为触发漏洞创建了条件:

  • CVE-2011-2140 补丁比较

    日期:2012-02-18 | 分类:软件漏洞

    CVE-2011-2140(Adobe Flash Player MP4 SequenceParameterSetNALUnit Buffer Overflow)主要是在解析MP4文件中的Sequence Parameter Set(spsunit)时,当spsunit中的pic_order_cnt_type为1时,num_ref_frames_in_pic_order_cnt_cycle字段就会被作为循环计数器将 offset_for_ref_frame上的数据复制到一块固定的栈上,但由于num_ref_frames_in_pic_order_cnt_cycle缺乏有效的检测,导致最终发生栈溢出。Exploit代码可在exploit-db上找到:http://www.exploit-db.com/exploits/18479,其中只接利用 0x0c0c0c0c 覆盖SEH来实现EIP控制。下面是补丁比较的情况,漏洞补丁增加了对num_ref_frames_in_pic_order_cnt_cycle数值的限制,要求其数值必须小于0xFF,同时大于0:

     

  • CVE-2011-2462 补丁分析

    日期:2012-02-12 | 分类:软件漏洞

    CVE-2011-2462(Adobe Reader U3D memory corruption)主要是对new分配的堆块未初始化,导致重用了之前分配的内存(该地址数据刚好来自u3d文件)。关于该漏洞的调试分析,可参见instruder和wingdbg分别写的两篇文章。在Adobe给出的补丁中,它重新添加了对新分配内存进行零初始化,这次刚好看雪上爆了bindiff 4.0的补丁比较神器,就拿来试用下,感觉比其它同类免费工具更强大多了,显得更文艺些:


  • CVE-2012-0003漏洞分析与修复

    日期:2012-02-11 | 分类:软件漏洞

     

    作者:riusksk(泉哥)

    主页:http://riusksk.blogbus.com

     

    漏洞公告

     

    漏洞分析

             首先分配大小为0x400的堆空间:

    这时分配的堆空间,我们标记为vulBuff。通过exploit-db上的Msf代码可以知道winmm.dll是在解析midi文件”MTrk”域中的event时,若处理的是Note On0x9X)或者Note Off0x8X)事件时就可能导致溢出。上面的Msf代码使用以下事件用于触发漏洞:

    根据midi文件格式可以知道Note On事件应该是以9X XX XX XX这种形式开头的4字节:

    End of Track应当为FF 2F 00

    所以Msf利用代码中用于触发漏洞的Note On事件9F B2 73 00,即0x0073b29f

    接下来读取事件:


    继续执行下去:

    跳转后:

    综上所述,此时EAX = ((9F & 0Fh) * 2^7 + B2) / 2 = 0x419,后面又会用这数据来索引原分配的堆空间vulBuff,由于分配的堆空间大小为0x400 < 0x419,因此最终会造成堆溢出访问:

    如果是Note Off0x8X)事件,它会减1后再写回:

    关于该漏洞的利用,VUPEN已经给出了详细描述。主要是创建个select对象,并设置一些属性,但只有一个属性是string,其余均为object,目的就是利用漏洞上面加1的特性,将string属性值0x8改成object属性值0x9,这样的话,原string类型的数据就会被当虚表指针来引用,因为对象的头4字节就是虚表指针,进而控制程序执行流程。更为具体的分析可参见VUPEN官方博文《Advanced Exploitation of Internet Explorer Heap Overflow Vulnerabilities (MS12-004)》以及4b_4b大牛的分析文章《Analysing the POC of CVE-2012-0003》,里面有具体的调试分析过程。

     

    漏洞修复

             通过补丁比较可以发现在计算相对堆空间的偏移量时多加了一条指令and bl,7Fh

    那么此时偏移量的计算公式为EAX = ((9F & 0Fh) * 2^7 +(B2&7Fh)) / 2 = 0x3D9,小于0x400,不再造成越界访问:

     

  • CVE-2010-4344漏洞分析与修复

    日期:2012-01-14 | 分类:软件漏洞

     

    作者:riusksk(泉哥)

    主页:http://riusksk.blogbus.com

     

    漏洞公告

    Exim是一个开放源代码的免费邮件传送软件,可以在Unix下提供邮件传输代理功能(MTA),由剑桥大学发布和维护。 Exim <= 4.69版本号的string.c文件中的string_vformat函数中存在基于堆的缓冲区溢出漏洞。远程攻击者可以借助包括两个邮件命令结合包含超大特制头消息的SMTP会话执行任意代码。该漏洞将导致不正确地拒绝登陆。

     

    漏洞分析

    本漏洞是由”string_vformat()”函数引发,其在处理经恶意构造的email messageheader时,由于缺乏有效地过滤,导致缓冲区溢出的发生。Exim使用各种自定义的函数来处理字符串,此可参见”exim-4.69/src/string.c”文件中的源码。

     00001054 BOOL

    00001055 string_format(uschar *buffer, int buflen, char *format, ...)

    00001056 {

    00001057 BOOL yield;

    00001058 va_list ap;

    00001059 va_start(ap, format);    // 可变参数ap,后面传参予string_vforamt函数,其中第1个参数指向format参数起始地址

    00001060 yield = string_vformat(buffer, buflen, format, ap);    // 漏洞函数

    00001061 va_end(ap);

    00001062 return yield;

    00001063 }

    00001064

    00001065

    00001066 BOOL

    00001067 string_vformat(uschar *buffer, int buflen, char *format, va_list ap)

    00001068 {

    00001069 enum { L_NORMAL, L_SHORT, L_LONG, L_LONGLONG, L_LONGDOUBLE };

    00001070

    00001071 BOOL yield = TRUE;

    00001072 int width, precision;

    00001073 char *fp = format;    // 字符串格式       /* Deliberately not unsigned */

    00001074 uschar *p = buffer;    // p指针指向buffer

    00001075 uschar *last = buffer + buflen - 1;    // last指针指向buffer+buflen-1

    ……省略……

    00001090   /* Non-% characters just get copied verbatim */

    00001091   // 遇到控制符%则继续读取下一字符

    00001092   if (*fp != '%')

    00001093     {

    00001094     if (p >= last) { yield = FALSE; break; }

    00001095     *p++ = (uschar)*fp++;

    00001096     continue;

    00001097     }

    00001098

    00001099   /* Deal with % characters. Pick off the width and precision, for checking

    00001100   strings, skipping over the flag and modifier characters. */

    00001101

    00001102   item_start = fp;

    00001103   width = precision = -1;    // 将变量”width”和”precision”均设为-1

    ……省略……

    00001157   /* Handle each specific format type. */

    00001158

    00001159   switch (*fp++)

    00001160     {

    00001161     case 'n':

    00001162     nptr = va_arg(ap, int *);

    00001163     *nptr = p - buffer;

    00001164     break;

    ……省略……

    00001226

    00001227     case 'c':

    00001228     if (p >= last) { yield = FALSE; goto END_FORMAT; }

    00001229     *p++ = va_arg(ap, int);    // 处理完%c之后,p指针继续向后偏移读取可变参数ap的下一个参数,但此时last并没有跟着偏移,这就有可能造成 p=last,导致后面的悲剧发生!

    00001230     break;

    ……省略……

    // 当fp指针即format参数为%s时

    00001237     case 's':

    00001238     case 'S':                   /* Forces *lower* case */

    00001239     s = va_arg(ap, char *);

    00001240

    00001241     INSERT_STRING:              /* Come to from %D above */

    00001242     if (s == NULL) s = null;

    00001243     slen = Ustrlen(s);   // 使用Ustrlen计算出ap的字符串长度,此值并非前面buflen参数

    00001244

    00001245     /* If the width is specified, check that there is a precision

    00001246     set; if not, set it to the width to prevent overruns of long

    00001247     strings. */

    00001248

    00001249     if (width >= 0)

    00001250       {

    00001251       if (precision < 0) precision = width;

    00001252       }

    00001253

    00001254     /* If a width is not specified and the precision is specified, set

    00001255     the width to the precision, or the string length if shorted. */

    00001256

    00001257     else if (precision >= 0)

    00001258       {

    00001259       width = (precision < slen)? precision : slen;

    00001260       }

    00001261

    00001262     /* If neither are specified, set them both to the string length. */

    00001263

    00001264     else width = precision = slen;   // 如果 width与precision均小于0(默认为-1),则width与precision置为slen。

    00001265

    00001266     /* Check string space, and add the string to the buffer if ok. If

    00001267     not OK, add part of the string (debugging uses this to show as

    00001268     much as possible). */

    00001269

    00001270     if (p >= last - width)    // 上面令width与precision为假,就是为了能够执行到这里

    00001271       {

    00001272       yield = FALSE;

    00001273       width = precision = last - p - 1;    // 由上可知我们能够令last = p,此时width与precision均会被设置为-1

    00001274       }

    00001275     sprintf(CS p, "%*.*s", width, precision, s);    // 将width与precision作为参数调用sprintf,s为可变参数列表ap,在ap参数中构造长字符串,这里p指针向malloc分配的堆块,最后导致堆溢出!

    00001276     if (fp[-1] == 'S')

    00001277       while (*p) { *p = tolower(*p); p++; }

    00001278     else

    00001279       while (*p) p++;

    00001280     if (!yield) goto END_FORMAT;

    00001281     break;

    如上所示,函数”string_vformat()”以一个超长的字符串作为其参数,导致了可利用的堆溢出漏洞发生。为了远程利用此漏洞,攻击者可以发送一封经恶意构造的email消息,由于该邮件会被拒绝,因此接着Exim会调用“log_write()  [exim-4.69/src/log.c] 函数进行日志记录,该函数会先调用malloc分配 0x2000 字节大小的缓冲区,用于存储错误信息,然后将分配的内存地址作为参数传递给“string_format()”,而函数“string_format()”仅是对“string_vformat()”函数的封装,最终造成堆溢出!

     

    漏洞修复

             添加对plast的判断,当它们相等时直接跳至函数尾部返回,同时对widthprecision进行判断,当它们为假时,将其设为0,进而避免堆溢出的发生。左下图是修复前的代码,右下图是修复后的代码:


  • CVE-2011-0065 漏洞分析与修复

    日期:2012-01-08 | 分类:软件漏洞

     

    作者:riusksk(泉哥)

    主页:http://riusksk.blogbus.com

     

    漏洞公告

     

    测试代码

    (poc.html:来源于instruder写的文档)

    <html>

    <body>

    <object id="d"><object>

    <script type="text/javascript">

    var e;

    e=document.getElementById("d");

     

    e.QueryInterface(Components.interfaces.nsIChannelEventSink).onChannelRedirect(null,new Object('0c'),0);

    e.data = "";

     

    </script>

    </body>

    </html>

    漏洞分析

    ImmDbg加载Firefox运行,并打开poc.html文件,触发异常:

    此时栈顶情况如下:

    0013F4FC   5A4A4E75  uNJZ  RETURN to xul.5A4A4E75

    跳过去:

    根据C++成员函数this指针调用约定,可以知道上面的ecx为虚表基址,eax为对象地址,而call dword ptr ds:[ecx+18] 调用的正是某个对象的方法,即虚函数。查看0x5A4A4E72地址属性,主要是由xul.dll模块中的函数导致的漏洞:

    0x5A4A4E66下条件断点:

    执行后:

    这里只引用一次对象就崩溃了,这就更加方便我们调试跟踪了,此时eax=0x05875470(对象地址),ecx=0x04c7b000(虚表基址),正是这里的虚表地址导致后续的异常发生。

    通过之前poc.html中的代码可以知道触发漏洞的关键函数位于onChannelRedirect(),它主要用于释放mChannel对象。我们搜索所有模块中的名称,找到这一函数(必须先加载Firefox符号表):

    对该函数下断(通常设置条件断点得知其也只是释放一次对象,因此这里直接对函数下断),运行后栈参数情况如下(每次运行上面的地址都是不一样,读者可以像instruder兄弟提出的方法:保存虚拟机镜像,需要时再从新加载运行,这样就保证每次的地址都一样,不过这里我没有这样做,所以后面的地址会有所不同):

    继续执行(保留前面的条件断点0x5A4A4E66,相当于这里的0x5AA74E66):

    由上可知,首先对象地址0x06898D70作为参数传递给OnChannelRedirect函数进行内存释放,而后面该对象地址又被引用导致释放重引用漏洞(use after free)的发生。

     

    漏洞修复

    添加对mChannel对象的判断,若对象被释放则直接返回函数,不再引用mChannel对象。

     

  • CVE-2011-2371漏洞分析与修复

    日期:2012-01-07 | 分类:软件漏洞

    作者:riusksk(泉哥)

    主页:http://riusksk.blogbus.com

     

    漏洞公告

    测试代码

    其中的关键代码如下(完整代码:http://www.exploit-db.com/exploits/17974/):

    spray();

    hola = new Array;

    hola.length = 2197815302;  // 相当于0x83000006

    w00t = function ph33r(prev, myobj, indx, array) {

      alert(myobj[0]); // trigger getProperty

    }

    hola.reduceRight(w00t,1,2,3);

     

    漏洞分析

           之前K_Khttp://hi.baidu.com/4b_4b/blog/item/a00c092e394cd74e9922ede9.html)已经在博客上做过分析,读完感觉这个漏洞比较典型,有一定的学习价值,于是就自己动手调试下,毕竟“纸上得来终觉浅,绝知此事要躬行。”

           通过公告可以知道漏洞主要是由Array.reduceRight()函数导致的,我们在OD中搜索所有模块中的名称,最后找到js3250!array_reduceRight函数(调试前须加载Firefox符号表,否则无法认识出函数名):

    接着在OD中直接对该函数下断

    bp array_reduceRight

    FireFox打开exploit.html文件,运行后断下:


    跟进array_extra函数:

    接着将0x83000006-1=0x83000005作为参数传给GetArrayElement,用于获取数组元素对应的数值。这里0x83000005是无符号数,但当它传递给GetArrayElement时,它被当作有符号数来处理,即负数-2097151995,此时作为索引号来获取数组元素值就造成了数组越界访问:

    跟进GetArrayElement函数,它通过ecx+edx*4来获得数组元素地址0x0C000014,此地址已经通过Heap Spray传送了可控数据0x0C000048

    0x0C000048下内存访问断点,F9之后:

    跳转后:

     

    漏洞修复

    将数组索引号与数组长度统一为无符号整数类型:

     

     

  • 悬挂指针(Dangling Pointer)

    日期:2012-01-01 | 分类:软件漏洞

     

    导致悬挂指针的两个经典场景:

    1、对已释放的C++对象进行重引用,即释放重引用漏洞;

    2、函数返回本地变量指针,但该变量只在函数内部定义,导致函数执行完后此指针就变成无效指针了。

     

    对象的使用

    使用虚函数的对象都会包含虚表指针,该指针位于对象内存区域的首个DWORD值(Pwn20wn 2010 黑客大赛上 Peter vreugdenhil 利用MS11-002漏洞在 Windows 7 下攻下 IE8 浏览器,他正是正是C++对象前面的虚表指针来绕过ASLR),后面是对象成员。通过虚表指针获得虚表地址,虚表里面包含有各个虚函数地址,通过虚函数地址可找到对应的虚函数代码。

     

    示例代码:

    ...

    mov ecx,objectaddr ; 分配对象地址给ecx

    ...

    mov eax,[ecx] ; 将ecx指向的内存区域的首个dword值赋予eax,此时eax即虚表指针。

    ...

    call [eax+8] ; 其中8是将执行的虚函数在虚表中偏移地址,这里对应的是第3个虚函数。

    ...

     

    利用思路

     

    覆盖虚表指针

    比如设法令上面的eax=0x0C0C0C04,那么最后就会去执行0x0C0C0C0C(利用heap spray实现);为了覆盖虚表指针,可在C++对象被释放后申请同等大小的内存,以占位被释放对象所在的内存区域。除了跳去0x0C0C0C0C外,我们还可设法令call [eax+8] = call/jmp ecx,同时我们将shellcode放置在对象内存区域中,执行call/jmp ecx后就会进入对象内存区域去执行ShellCode。

     

    实例:CVE-2011-0065:Firefox 3.6.16 mChannel 释放重引用漏洞

    http://bbs.pediy.com/showthread.php?p=1034525#post1034525

     

    当存在继承类时,对象实例中就存在两个虚表指针,此时就无需去占位被释放对象的内存了,我们可以去覆盖第二个虚表指针。

     

  • CVE-2010-2883漏洞分析与修复

    日期:2011-12-04 | 分类:软件漏洞

    【标题】:CVE-2010-2883漏洞分析与修复

    【作者】:riusksk(泉哥)

    【时间】:2011年10月3日

     

    漏洞描述

           Adobe Reader的CoolType.dll库在解析字体文件SING表格中的uniqueName项时存在栈溢出漏洞,用户受骗打开了特制的PDF文件就可能导致执行任意代码。目前这个漏洞正在被多个恶意软件积极的用于挂马攻击。

     

    漏洞分析

     

    用IDA反汇编CoolType.dll库,查看字符串可发现“SING”字体,直接定位进去即可查看到该库对sing表格的解析方式,主要是strcat造成的溢出漏洞:


    .text:0803DCF9                 push    ebp

    .text:0803DCFA                 sub     esp, 104h       ; 分配栈空间0x104

    .text:0803DD00                 lea     ebp, [esp-4]    ; 后面的strcat会把执行结果保存在ebp中

    .text:0803DD04                 mov     eax, dword_8230FB8

    .text:0803DD09                 xor     eax, ebp

    .text:0803DD0B                 mov     [ebp+104h], eax

    .text:0803DD11                 push    4Ch

    .text:0803DD13                 mov     eax, offset loc_8184A54

    .text:0803DD18                 call    __EH_prolog3_catch

    .text:0803DD1D                 mov     eax, [ebp+arg_C]

    .text:0803DD23                 mov     edi, [ebp+arg_0]

    .text:0803DD29                 mov     ebx, [ebp+arg_4]

    .text:0803DD2F                 mov     [ebp+var_28], edi

    .text:0803DD32                 mov     [ebp+var_30], eax

    .text:0803DD35                 call    sub_804172C

    .text:0803DD3A                 xor     esi, esi

    .text:0803DD3C                 cmp     dword ptr [edi+8], 3

    .text:0803DD40                 mov     [ebp+var_4], esi

    .text:0803DD43                 jz      loc_803DF00

    .text:0803DD49                 mov     [ebp+var_1C], esi

    .text:0803DD4C                 mov     [ebp+var_18], esi

    .text:0803DD4F                 cmp     dword ptr [edi+0Ch], 1

    .text:0803DD53                 mov     byte ptr [ebp+var_4], 1

    .text:0803DD57                 jnz     loc_803DEA9

    .text:0803DD5D                 push    offset aName    ; "name"

    .text:0803DD62                 push    edi

    .text:0803DD63                 lea     ecx, [ebp+var_1C]

    .text:0803DD66                 mov     [ebp+var_11], 0

    .text:0803DD6A                 call    sub_80217D7

    .text:0803DD6F                 cmp     [ebp+var_1C], esi

    .text:0803DD72                 jnz     short loc_803DDDD

    .text:0803DD74                 push    offset aSing    ; "SING"

    .text:0803DD79                 push    edi

    .text:0803DD7A                 lea     ecx, [ebp+var_24] ; 指向sing表入口

    .text:0803DD7D                 call    sub_8021B06     ; 处理SING表

    .text:0803DD82                 mov     eax, [ebp+var_24]

    .text:0803DD85                 cmp     eax, esi        ; 判断是否为空

    .text:0803DD87                 mov     byte ptr [ebp+var_4], 2

    .text:0803DD8B                 jz      short loc_803DDC4 ; 这里不跳转

    .text:0803DD8D                 mov     ecx, [eax]      ; 字体资源版本号,这里为1.0版本,即00 10 00 00

    .text:0803DD8F                 and     ecx, 0FFFFh

    .text:0803DD95                 jz      short loc_803DD9F ; 这里跳转

    .text:0803DD97                 cmp     ecx, 100h

    .text:0803DD9D                 jnz     short loc_803DDC0

    .text:0803DD9F

    .text:0803DD9F loc_803DD9F:                            ; CODE XREF: sub_803DCF9+9Cj

    .text:0803DD9F                 add     eax, 10h        ; 相对sing表入口偏移0x10处找到uniqueName

    .text:0803DDA2                 push    eax             ; uniqueName域

    .text:0803DDA3                 lea     eax, [ebp+0]

    .text:0803DDA6                 push    eax             ; 目的地址是一段固定大小的栈空间

    .text:0803DDA7                 mov     byte ptr [ebp+0], 0

    .text:0803DDAB                 call    strcat          ; 造成溢出!!!

    漏洞修复

             官方在修补该漏洞时,添加对字符串长度的检测与限制,这里它用sub_813391E函数代替了原来的strcat函数:


    跟进sub_813391E函数:

  • rop seh 跳板指令

    日期:2011-05-22 | 分类:软件漏洞

    欢迎大家继续补充:

     

    # mov reg32, [esp+8] # call [reg32+offset]

    # mov reg,[ebp+0c] # call [reg] 

    # mov reg, fs:[0] # ... # ret 

    # pop regX # pop regY # pop regZ #call [regZ+8] // 此时regZ指向nextseh

    # pop regX # pop regY # pop esp # ret // 此时esp指向nextseh

    # push regX # mov regY,[esp+0xc] # call [regY+0xc] 

    # mov regY,[ebp+0xc] # push [regY-0x4] # ret 

    # mov regY, fs:[0] # mov reg, [regY-0x8] # jmp reg

    # add esp,offset # ret

    # xchg reg,esp # ret // reg指向可控栈地址

    # xchg esp,reg # reg

    # push reg, pop esp # ret

    # mov esp,reg # ret

    # call [reg]

    # add ebp, [reg+x]  # leave # ret (谢谢lisl03兄弟补充)