`

缓冲区溢出——安全漏洞的发源地

阅读更多

高质量程序设计艺术》样章连载——3.2 缓冲区溢出

<o:p> </o:p>

原书名:Code Quality: The Open Source Perspective

<o:p></o:p>

<!---->1.         <!---->深入剖析著名开源软件的质量问题<o:p></o:p>

<!---->2.         <!---->全面阐述CC++Java代码中的常见编程错误<o:p></o:p>

<!---->3.         <!---->指导你编写优秀代码的圣经<o:p></o:p>

<o:p> </o:p>

更多详细信息http://www.china-pub.com/37661<o:p></o:p>

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

3.2  缓冲区溢出

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

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

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

/*

h0h0h0 O-day k0d3z

Scrippiedvorakjimjones的帮助下开发

<!---->


<!---->

<!---->[1]<!----> netbsdsrc/libexec/ftpd

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

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

<!----> <!---->填充(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命令,使得pwdreplydirname函数被调用。请注意,在pwdpath变量与replydirnamenpath变量中储存的是相同的目录名。

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>

此时%esp0xd00006f0<o:p></o:p>

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

返回到pwd0x8048674<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>

此时%esp0xd0000b10<o:p></o:p>

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

返回到yyparse0x8052000<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>
分享到:
评论

相关推荐

    缓冲区溢出——栈溢出

    4. **代码审查**:定期进行源代码审计,找出潜在的溢出漏洞,并及时修复。 5. **编译器选项**:利用编译器的防护特性,如GCC的 `-fstack-protector`,来检测栈溢出并防止攻击。 总之,理解栈溢出的原理和防范措施...

    东南大学网络空间安全实验基础——缓冲区溢出漏洞实验

    东南大学网络空间安全实验基础——缓冲区溢出漏洞实验 该实验报告的主要目的是了解缓冲区溢出漏洞的原理和实验过程。缓冲区溢出漏洞是一种常见的安全漏洞,攻击者可以通过溢出缓冲区来 inject恶意代码,从而控制...

    防止缓冲区溢出——C_C++语言的安全问题

    2、缓冲区溢出的产生 3、缓冲区溢出的后果 4、通过安全的编程技术来防止技术措施 4.1 C编程中的主要陷阱 4.2 避免使用gets() 4.3 慎重使用strcpy() 4.4 慎重使用strcat(),sprintf()和vsprintf() 4.5 慎重使用...

    缓冲区溢出漏洞实验报告(附带程序源码和被攻击程序)

    3.缓冲区溢出演示 4.溢出攻击结果与危害 5.防御手段 适合人群: 具备一定编程基础,作业时间不够的学生,对缓冲区溢出原理不了解的初学者 环境: IDA pro7.6, vc++ ,x32dbg。 阅读建议: 有一点 x32dbg逆向工具...

    缓冲区溢出实验

    - **安全意识**:通过本实验,加深了对缓冲区溢出这一安全漏洞的理解,并认识到在编程时必须谨慎处理数据输入,避免此类漏洞的发生。 - **技术提升**:掌握了如何利用缓冲区溢出进行攻击的基本原理和方法,为进一步...

    《缓冲区溢出攻击—检测、剖析与预防》

    在IT行业中,缓冲区溢出是一种由于程序处理数据时,超过了预分配内存空间的限制而导致的安全漏洞。这种攻击方式能够导致系统崩溃、数据丢失,甚至允许攻击者获得系统的控制权。 缓冲区溢出的发生通常源于编程错误,...

    Windows下缓冲区溢出漏洞的利用

    鉴于缓冲区溢出漏洞的严重性及其对系统安全构成的威胁,采取有效的防范措施至关重要: - **安全编程实践**:开发者应采用安全的编程习惯,如使用安全的字符串处理函数,定期进行代码审查,及时修补已知漏洞。 - **...

    windows 缓冲区溢出

    缓冲区溢出是计算机安全领域的一个重要话题,尤其在Windows操作系统中,由于其广泛的应用,对缓冲区溢出的理解和防范显得尤为重要。缓冲区溢出通常发生在程序处理数据时,当输入的数据超过了预分配的内存空间(即...

    C语言源程序的缓冲区溢出漏洞分析及解决方案

    ### C语言源程序的缓冲区溢出漏洞分析及解决方案 #### 概述 缓冲区溢出一直是黑客攻击的重要手段之一,特别是在C/C++程序中尤为常见。这是因为C/C++提供了许多可以直接访问内存底层的函数,如果程序员未能妥善处理...

    缓冲区溢出攻击实例

    缓冲区溢出攻击的原理是基于缓冲区溢出漏洞,该漏洞是由于程序在处理用户输入时没有正确地检查缓冲区的大小,导致缓冲区溢出,从而使攻击者能够inject恶意代码。 MS05-039 漏洞是 Microsoft Windows 即插即用功能中...

    缓冲区溢出光速入门 缓冲区溢出基基础

    缓冲区溢出是一种常见的安全漏洞,尤其在使用C/C++等低级语言编程时更容易出现。当程序试图将更多的数据写入到一个固定大小的内存区域(即缓冲区)中时,就会发生缓冲区溢出。例如,在代码中定义了一个整型数组`int ...

    2019网络空间安全国赛8080缓冲区溢出漏洞

    在网络安全领域,缓冲区溢出漏洞是一种常见的攻击手段,它主要发生在程序处理数据时,由于对输入数据长度的检查不足,导致数据写入了原本并未分配的空间,进而可能破坏程序的正常运行,甚至允许攻击者执行任意代码。...

    Q版缓冲区溢出教程 学习缓冲区溢出的好教材

    缓冲区溢出是计算机安全领域中的一个重要概念,尤其在编程和系统安全方面具有深远的影响。Q版缓冲区溢出教程,正如其名,是一种以轻松、直观的方式介绍这一技术的教材,适合初学者和对安全感兴趣的IT从业者学习。本...

    《网络安全技术》大作业:缓冲区溢出实验报告

    一、缓冲区溢出原理 缓冲区溢出是因为在程序执行时数据的长度超出了预先分配的空间大小,导致覆盖了其他数据的分配区域,从而执行非授权指令,获取信息,取得系统特权进而进行各种非法操作导致程序运行失败、系统宕...

    2019网络空间安全国赛8080缓冲区溢出漏洞.rar

    《2019网络空间安全国赛8080缓冲区溢出漏洞》 网络空间安全,作为现代信息化社会的核心议题,一直以来都是全球关注的焦点。2019年网络空间安全国赛,旨在考察中职学生对于网络安全技术的理解与应用能力,其中8080...

    Q版缓冲区溢出教程(电子书版)

    《Q版缓冲区溢出教程》是一本深入探讨计算机安全领域中重要问题——缓冲区溢出的电子书。缓冲区溢出是编程错误的一种常见形式,尤其在C和C++等语言中尤为突出,它可能导致程序崩溃,或者被黑客利用进行攻击。本教程...

    安全编程之缓冲区溢出.7z

    缓冲区溢出是其中最常见的安全漏洞之一,它可能导致数据丢失、程序崩溃,甚至恶意代码执行,对系统安全构成严重威胁。本主题将深入探讨缓冲区溢出的原理、影响以及如何通过安全编程技术防止它。 ### 缓冲区溢出概述...

Global site tag (gtag.js) - Google Analytics