Adobe Reader CVE-2010-2883分析
xinali opened this issue · 1 comments
CVE-2010-2883分析
这个漏洞分析的很迷茫,很蛋疼。漏洞原因用了一天也就熟悉了,但是为了找到从stacat
开始到触发shellcode
,用了接近半个月的时间。想了各种各样的方法也没有解决到底是怎么触发的。
环境
windows xp sp3
Windows Debugger Version 6.11.0001.404 X86
分析
根据exploit-db问题的根源出在CoolType.dll
在解析SING Table
时造成溢出,
简单的来看一下CoolType.dll
对SING
的解析伪码,理解一下原理
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查看一下字体文件
在具体分析一下SING Table
数据
根据github上的解析库afdko可以找到SING Table
的具体定义
根据定义,可以发现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
偏移16
字节处正好是uniqueName
,根据msdn
没有经过验证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;
}
对应的汇编代码
超过0x104
肯定会出错,具体出错的位置,我始终无法跟踪到!其实这里从9.4.0
的修复版本中也能看到
其中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;
}
不过可以根据这个确定一下溢出长度
即使不知道具体的出错位置,+8
的位置是将来跳转的位置,这个是可以确定的。
其实出错的原因,我能想到的无非就是两种
-
SEH handler
测试过
SEH handler
,在我有限的知识体系里,应该先调用ntdll!KiUserExceptionDispatcher
在这里下断点,但是却没有断到 -
虚函数指针
这里在函数
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 preview
的TTD
调试,但是遇到了这个问题,最终都没有确定具体的原因,很伤。再来说一下
shellcode
,首先需要利用icucnv36
模块,因为其在各个版本中的地址是一样的,可以将+8
的位置溢出到该模块,从而绕过DEP
,但是有一点需要注意,icucnv36
模块中没有常规的直接绕过DEP
的函数,可以利用其中的CreateFileA -> CreateFileMapping -> MapViewOfFile -> memcpy
这种方法其实在我日常分析恶意代码中比较常见。具体怎么利用就不细说了,网上都有。
到这里,该漏洞就分析完了
参考
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