3.2  缓冲区溢出_Android, Python及开发编程讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Android, Python及开发编程讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3872 | 回复: 0   主题: 3.2  缓冲区溢出        下一篇 
barry
注册用户
等级:中校
经验:1534
发帖:236
精华:2
注册:2012-1-13
状态:离线
发送短消息息给barry 加好友    发送短消息息给barry 发消息
发表于: IP:您无权察看 2012-2-10 15:28:56 | [全部帖] [楼主帖] 楼主

3.2  缓冲区溢出

北京联动北方科技有限公司在检查C/C++代码时,最常见到的安全漏洞就是缓冲区溢出(buffer overflow或buffer overrun)。对已经被利用的安全漏洞所做的分析表明,多达50%的漏洞源于缓冲区溢出。缓冲区溢出产生的原因是粗心的代码在特定数据结构的范围之外进行写入操作。不管使用何种语言,不小心的话都会出现这种问题:一段错误的代码序列对程序维护的数据结构的错误位置进行写入操作,导致程序工作不正常。在C/C++程序中,这个问题尤其严重,原因是当前大多数C/C++编译器与运行时环境的设计,允许使用指针和数组下标对应用的数据做不受限制的访问,并且还允许执行位于应用数据区与栈区的代码。因此,在一般的C/C++程序中,一个指向数组范围之外的数组下标将会存取对应内存位置处的数据,而且是可读可写的。同样的情况,如果是Java程序的话,会导致ArrayIndexOutOfBoundsException异常;如果是Perl程序的话,会自动增加数组的大小。

攻击者利用程序中的缓冲区溢出,在执行该程序的计算机上执行任意代码的方法有很多种。最简单的办法是直接将一个位于栈上的(函数局部的)缓冲区末端的函数返回地址覆盖,让它指向该缓冲区中特别编制的代码。还有许多其他方法也可以(而且已经)被用来执行攻击者的代码,比如利用储存在堆中的缓冲区以及储存在静态存储中的缓冲区。基于本书的宗旨——以真正在使用的代码为例——现在让我们来看一下NETBSD与OpenBSD版本的FTP服务器ftpd[1]的一个单字节偏移(off-by-one)错误是如何被利用,使得攻击者可以在宿主机上获得命令行访问的。我们的目的不是要详细解释缓冲区溢出的工作原理——如果你确实感兴趣的话,请参阅Viega与McGraw的著作[VM01]的第7章,而是想要通过一个详尽的例子来告诉读者,即使是一个很小的错误也能够让攻击者获得对整个系统的控制。

与这个漏洞相关的代码如图3-1所示。函数replydirname将会把name复制到npath,将所有的双引号使用一个额外的双引号进行转义。问题在于,尽管代码在基本的for循环里面很小心地进行处理,保证写入的范围不超出npath缓冲区的范围,但是在添加作为转义符的双引号时(图3-1:1)却没有进行边界检查:写入位置是否已经超出了缓冲区的大小。其结果就是,如果一个1 024字符(MAXPATHLEN)的目录名以一个双引号字符结束,用来表示字符串结束的’\0’字符会被写入到缓冲区外去,这也就是如下的攻击代码的工作原理:

/*
h0h0h0 O-day k0d3z


由Scrippie在dvorak与jimjones的帮助下开发

北京联动北方科技有限公司
北京联动北方科技有限公司


[1] netbsdsrc/libexec/ftpd。

为了有助于理解为什么这个看起来没什么大不了的单字节偏移错误能够让攻击者在你的系统上执行他们的代码,我们需要研究一下在程序执行时其栈的布局(另请参见5.6节)。表3-3给出了在replydirname即将返回调用者pwd时,栈的布局。如你所见,函数被调用的时候,其栈框架上储存着传入的参数、保存的上一个栈框架的框架指针(通常是一个寄存器——这里是%ebp——用于访问框架的内容),以及局部变量(也请参阅5.6.1节)。针对ftpd的攻击包括构造并使用一个包含以下各个部分的1 024字符的目录名:

■ 一个本地可写的目录(例如/pub/incoming)的绝对路径名,这保证了目录名可以通过一系列的MKDIR与CHDIR命令逐步构造,而且不会一开始就触发单字节偏移错误。

■ 填充(AAAA/AAAA…)字节,以确保目录名有1 024字节长。

■ 攻击者想要在被攻击的计算机上执行的代码(\x31\xc0\x89\xc1\x80\xc1"…),该段代码的功能是通过FTP连接提供命令行访问。

■同一个返回地址的多个实例,该返回地址用于将程序流程改变到执行攻击代码(\x4f\x09\x00\xd0\x4f\x09\x00\xd0"…——请看后面的解释)。

■ 填充更多字节,以一个双引号结尾(AAA"),用来触发偏差为一错误。

攻击所使用的路径的完整结构如表3-3所示,从内存地址0xd0000710开始。攻击代码通过一个FTP连接创建具有如前所述名字的目录,(通过一系列的CHDIR命令)改变当前目录为所生成的目录,然后调用PWD命令,使得pwd与replydirname函数被调用。请注意,在pwd的path变量与replydirname的npath变量中储存的是相同的目录名。

在pwd函数试图返回其调用者(yyparse)时,攻击者的代码就会被执行到。在i386体系结构上,从一个函数返回时需要对展开其栈框架,其操作如下:

<o:p> 北京联动北方科技有限公司</o:p>

<o:p>       </o:p>

表3-3  ftpd

缓冲区攻击发生时的栈<o:p></o:p>

地    址<o:p></o:p>

内    容<o:p></o:p>

值<o:p></o:p>

pwd的栈框架<o:p></o:p>

0xd0000b14<o:p></o:p>

0xd0000b10<o:p></o:p>

0xd0000b0f<o:p></o:p>

0xd0000b0e<o:p></o:p>

0xd0000b0d<o:p></o:p>

0xd0000b0c<o:p></o:p>

返回地址(yyparse + 3 583)<o:p></o:p>

保存的栈框架指针(%ebp)<o:p></o:p>

path[]的最后一个字节<o:p></o:p>

填充字节<o:p></o:p>

<o:p> </o:p>

目录路径分隔符<o:p></o:p>

0x8052000<o:p></o:p>

<o:p> </o:p>

A<o:p></o:p>

A<o:p></o:p>

A<o:p></o:p>

/<o:p></o:p>

pwd的栈框架<o:p></o:p>

0xd0000b08<o:p></o:p>

0xd0000b04<o:p></o:p>

0xd0000b00<o:p></o:p>

<o:p> </o:p>

0xd0000a10<o:p></o:p>

0xd0000a0c<o:p></o:p>

0xd0000a0b<o:p></o:p>

<o:p> </o:p>

0xd000094f<o:p></o:p>

0xd000094e<o:p></o:p>

<o:p> </o:p>

0xd0000710<o:p></o:p>

攻击者放置的返回地址<o:p></o:p>

攻击者放置的返回地址<o:p></o:p>

攻击者放置的返回地址<o:p></o:p>

…<o:p></o:p>

攻击者放置的返回地址<o:p></o:p>

目录路径分隔符<o:p></o:p>

攻击代码的最后一个字节<o:p></o:p>

…<o:p></o:p>

攻击代码的第一个字节<o:p></o:p>

填充字节<o:p></o:p>

…<o:p></o:p>

path[]的第一个字节<o:p></o:p>

0xd000094f <o:p></o:p>

0xd000094f<o:p></o:p>

0xd000094f<o:p></o:p>

0xd000094f<o:p></o:p>

0xd000094f<o:p></o:p>

/<o:p></o:p>

<o:p> </o:p>

<o:p> </o:p>

xor %eax, %eax<o:p></o:p>

A<o:p></o:p>

pub/incoming/AA…<o:p></o:p>

/<o:p></o:p>

replydirname的栈框架<o:p></o:p>

0xd00006fc<o:p></o:p>

0xd00006f8<o:p></o:p>

0xd00006f4<o:p></o:p>

0xd00006f0<o:p></o:p>

<o:p> </o:p>

<o:p> </o:p>

<o:p> </o:p>

0xd00006ef<o:p></o:p>

0xd00002f0<o:p></o:p>

0xd00002ec<o:p></o:p>

压栈的message参数<o:p></o:p>

压栈的name参数<o:p></o:p>

返回地址(pwd+116)<o:p></o:p>

保存的栈框架指针(%ebp)<o:p></o:p>

<o:p> </o:p>

<o:p> </o:p>

<o:p> </o:p>

npath[]的最后一个合法字节<o:p></o:p>

npath[]的第一个合法字节<o:p></o:p>

i<o:p></o:p>

“is the …”<o:p></o:p>

0xd0000710<o:p></o:p>

0x8048674<o:p></o:p>

0xd0000b10<o:p></o:p>

(正常情况)<o:p></o:p>

0xd0000b00<o:p></o:p>

(遭受攻击时)<o:p></o:p>

A<o:p></o:p>

/<o:p></o:p>

在表3-4中,可以看到从replydirname返回pwd,再从pwd返回其调用者yyparse时,这个操作是怎么进行的。现在看看当replydirname的代码将npath[]缓冲区后面的字节用0覆写时的情况。这个值为0的一个字节将被写入的地址就是replydirname储存其调用者(pwd)的栈框架指针的地方:0xd00006f0。其结果就是,保存的栈框架指针的最低字节从0x10变成了0x00,也就是对应的指针从0xd0000b10变成了0xd0000b00。现在让我们看看表3-5列出的新的返回序列。当replydirname返回到pwd的时候,它从栈上取出的栈框架指针的值0xd0000b00是错误的,而且pwd会使用这个值来恢复其栈指针。这个错误的栈指针值(0xd0000b00)指向path[]的内存区域,其位置恰好包含着可以给攻击者提供(他们垂涎已久的)对我们的机器的命令行访问能力的代码。故事结束了。

表3-4  展开栈(默认情况)<o:p></o:p>

地    址<o:p></o:p>

指    令<o:p></o:p>

说    明<o:p></o:p>

在replydirname + 183展开:<o:p></o:p>

0x80485f7<o:p></o:p>

0x80485f9<o:p></o:p>

0x80485fa<o:p></o:p>

mov %ebp, %esp<o:p></o:p>

pop %ebp<o:p></o:p>

ret<o:p></o:p>

此时%esp是0xd00006f0<o:p></o:p>

%ebp成为0xd0000b10<o:p></o:p>

返回到pwd(0x8048674)<o:p></o:p>

在pwd + 116展开:<o:p></o:p>

0x8048674<o:p></o:p>

0x8048677<o:p></o:p>

0x8048679<o:p></o:p>

0x804867a<o:p></o:p>

add $0x10, %esp<o:p></o:p>

mov %ebp, %esp<o:p></o:p>

pop %ebp<o:p></o:p>

ret<o:p></o:p>

<o:p> </o:p>

此时%esp是0xd0000b10<o:p></o:p>

%esp成为0xd0000b14<o:p></o:p>

返回到yyparse(0x8052000)<o:p></o:p>

在yyparse + 3 583继续:<o:p></o:p>

0x8052000<o:p></o:p>

jmp 0x8052123<o:p></o:p>

<o:p> </o:p>

表3-5  在遭受攻击时展开栈<o:p></o:p>

<t>




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论