xinali/articles

Adobe Reader CVE-2010-2883分析

xinali opened this issue · 1 comments

CVE-2010-2883分析

这个漏洞分析的很迷茫,很蛋疼。漏洞原因用了一天也就熟悉了,但是为了找到从stacat开始到触发shellcode,用了接近半个月的时间。想了各种各样的方法也没有解决到底是怎么触发的。

环境

根据exploitdb

windows xp sp3
Windows Debugger Version 6.11.0001.404 X86

分析

根据exploit-db问题的根源出在CoolType.dll在解析SING Table时造成溢出,
简单的来看一下CoolType.dllSING的解析伪码,理解一下原理

char __cdecl sub_803DCF9(int a1, _DWORD *a2, int a3, int a4)
{
  int v4; // edi
  bool v5; // zf
  int v6; // eax
  size_t v7; // eax
  int v8; // eax
  int v9; // eax
  int v10; // eax
  int v12; // eax
  int *v13; // ecx
  char v14; // [esp+10h] [ebp-58h]
  int v15; // [esp+30h] [ebp-38h]
  int v16; // [esp+38h] [ebp-30h]
  int v17; // [esp+3Ch] [ebp-2Ch]
  int v18; // [esp+40h] [ebp-28h]
  int v19; // [esp+44h] [ebp-24h]
  int v20; // [esp+48h] [ebp-20h]
  int v21; // [esp+4Ch] [ebp-1Ch]
  int v22; // [esp+50h] [ebp-18h]
  char v23; // [esp+57h] [ebp-11h]
  int v24; // [esp+64h] [ebp-4h]
  char v25; // [esp+68h] [ebp+0h]

  v4 = a1;
  v18 = a1;
  v16 = a4;
  sub_804172C();
  v5 = *(_DWORD *)(a1 + 8) == 3;
  v24 = 0;
  if ( !v5 )
  {
    v21 = 0;
    v22 = 0;
    v5 = *(_DWORD *)(a1 + 12) == 1;
    LOBYTE(v24) = 1;
    if ( v5 )
    {
      v23 = 0;
      sub_80217D7(&v21, a1, "name");
      if ( v21 )
        goto LABEL_52;
      sub_8021B06(&v19, a1, "SING"); // <--- 解析SING
      v6 = v19;
      LOBYTE(v24) = 2;
      if ( v19 )
      {
        if ( !(*(_DWORD *)v19 & 0xFFFF) || (*(_DWORD *)v19 & 0xFFFF) == 0x100 )
        {
          v25 = 0;
          strcat(&v25, (const char *)(v19 + 16)); // 问题函数
          sub_8001243(a2, (int)&v25);
          v6 = v19;
        }
        v23 = 1;
      }
    ...
  return 1;
}

v19的数据附加到v25末尾,很明显,如果v19没有做长度校验就很容易造成溢出。

pdf样本分析

具体SING字体相关的说明可以看一下adobe文档

现在来具体分析msf.pdf看看是如何造成溢出的,利用pdfstreamdumper提取出数据,010查看一下字体文件

1552988687765

在具体分析一下SING Table数据

1552988803334

根据github上的解析库afdko可以找到SING Table的具体定义

1552989003690

根据定义,可以发现uniqueName的偏移位置为16字节

typedef struct
{
    Card16 tableVersionMajor;           // +0 偏移
    Card16 tableVersionMinor;			// +2
    Card16 glyphletVersion;				// +4
    Card16 permissions;					// +6
    Card16 mainGID;						// +8
    Card16 unitsPerEm;					// +10
    Int16 vertAdvance;					// +12
    Int16 vertOrigin;					// +14
    Card8 uniqueName[SING_UNIQUENAMELEN]; // +16 
    Card8 METAMD5[SING_MD5LEN];
    Card8 nameLength;
    Card8 *baseGlyphName; /* name array */
} SINGTbl;

exploitdb说是uniqueName造成的溢出,我们在0803DD9F下断点,对照一下数据

.text:0803DD9F                 add     eax, 10h
.text:0803DDA2                 push    eax             ; char *
.text:0803DDA3                 lea     eax, [ebp+108h+var_108] // 即lea eax, [ebp]
.text:0803DDA6                 push    eax             ; char *
.text:0803DDA7                 mov     [ebp+108h+var_108], 0
.text:0803DDAB                 call    strcat

可以发现eax指向的就是SING Table

1552989254940

偏移16字节处正好是uniqueName,根据msdn

1552998441835

没有经过验证strcat会直接溢出[ebp],再来看一下ida反汇编代码

if ( !(*(_DWORD *)v19 & 0xFFFF) || (*(_DWORD *)v19 & 0xFFFF) == 0x100 )
{
    v25 = 0;
    strcat(&v25, (const char *)(v19 + 16));
    sub_8001243(a2, (int)&v25);
    v6 = v19;
}

对应的汇编代码

1553611248092

超过0x104肯定会出错,具体出错的位置,我始终无法跟踪到!其实这里从9.4.0的修复版本中也能看到

1553612940139

其中sub_813391E,长度肯定不会超过0x104



char *__cdecl sub_813391E(char *a1, char *a2, int a3)  // 修复中添加的函数
{
  size_t v3; // eax
  char *result; // eax

  v3 = strlen(a1);
  if ( a3 > v3 )
    result = strncat(&a1[v3], a2, a3 - v3 - 1);
  else
    result = a1;
  return result;
}

不过可以根据这个确定一下溢出长度

1553611571171

即使不知道具体的出错位置,+8的位置是将来跳转的位置,这个是可以确定的。

其实出错的原因,我能想到的无非就是两种

  1. SEH handler

    测试过SEH handler,在我有限的知识体系里,应该先调用ntdll!KiUserExceptionDispatcher在这里下断点,但是却没有断到

    1553611907374

  2. 虚函数指针

    这里在函数sub_8016BDE中的sub_801BB21看到了,调试了一下,但也不对

    int __cdecl sub_801BB21(int (__stdcall ***a1)(int, int, int, int, int, int), int a2, int a3, int a4, int a5, int a6, int a7)
    {
      int (__stdcall **v7)(int, int, int, int, int, int); // eax  <=====
      int result; // eax
    
      v7 = *a1;
      ++dword_823A6A0;
      result = (*v7)(a2, a3, a4, a5, a6, a7);
      if ( !(_BYTE)result )
        --dword_823A6A0;
      return result;
    }

    以上两种方法都尝试了,其中也尝试过用windbg previewTTD调试,但是遇到了这个问题,最终都没有确定具体的原因,很伤。

    再来说一下shellcode,首先需要利用icucnv36模块,因为其在各个版本中的地址是一样的,可以将+8的位置溢出到该模块,从而绕过DEP,但是有一点需要注意,icucnv36模块中没有常规的直接绕过DEP的函数,可以利用其中的

    CreateFileA -> CreateFileMapping -> MapViewOfFile -> memcpy

    这种方法其实在我日常分析恶意代码中比较常见。具体怎么利用就不细说了,网上都有。

    到这里,该漏洞就分析完了

参考

SING 结构定义

Brief Analysis On Adobe Reader SING Table Parsing Vulnerability (CVE-2010-2883)

泉哥的书

這兩天分析了這個漏洞,關於出錯位置在我的電腦上是(接續 sub_801BB21):
result = (*v7)(a2, a3, a4, a5, a6, a7); // 拿 **a1 也就是 0x808b116 當 function pointer

808b116:
0808B2E3 mov eax, [edi+3Ch] ; 從 strcat 就維持相同的值的 edi + 0x3c == 0x12e6d0
0808B308 call dword ptr [eax] ; *(0x12e6d0)

12e6d0 是被 overflow 過去的區域最後的部分,到這邊成功 control flow hijack

關於 12e6d0 是啥?為啥存放 function pointer 就沒什麼頭緒了,不過應該可以確定不是 seh 也不是 return addres