文章目录
  1. 内存溢出
  2. 内存泄漏
  3. 缓冲区溢出
  4. 缓冲区溢出攻击
    1. 植入
    2. 利用已存在的代码
    3. 控制程序转移
    4. 活动纪录
    5. 函数指针
    6. 代码植入和流程控

内存与缓冲区

内存溢出

要求分配的内存超出了系统目前所能分配给你的,系统不能满足需求,于是产生溢出。

内存泄漏

向系统申请的内存使用完了以后没有释放,结果那块内存的地址弄丢了,而系统也不能再次将它分配给需要的程序。

  1. 常发性内存泄漏。
    发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。

  2. 偶发性内存泄漏。
    发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。

  3. 一次性内存泄漏。
    发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。

  4. 隐式内存泄漏。
    程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。

缓冲区溢出

计算器向缓冲区内填充数据位数时超过了缓冲区本身的容量,溢出的数据覆盖在合法数据上。造成缓冲区溢出的原因是程序中没有仔细检查用户输入的参数。

缓冲区溢出攻击

往缓冲区中填东西造成它溢出一般只会出现 Segmentation fault,而不能达到攻击的目的。最常见的手段是通过制造缓冲区溢出使程序运行一个用户 shell,再通过 shell 执行命令。如果该程序有 root 执行权限的话,就获得了一个有 root 权限的 shell,可以对系统进行任意操作了。缓冲区溢出攻击之所以成为一种常见安全攻击手段其原因在于缓冲区溢出漏洞太普遍了,并且易于实现。而且,缓冲区溢出成为远程攻击的主要手段其原因在于缓冲区溢出漏洞给予了所想要的一切:植入并且执行攻击代码。被植入的攻击代码以一定的权限运行有缓冲区溢出漏洞的程序,从而得到控制权。

植入

向被攻击的程序输入一个字符串,程序会把这个字符串放到缓冲区里。这个字符串包含的数据是可以在这个被攻击的硬件平台上运行的指令序列。用被攻击程序的缓冲区来存放攻击代码,缓冲区可以设在任何地方:堆栈(stack,自动变量)、堆(heap,动态分配的内存区)和静态数据区。

利用已存在的代码

有时,代码已经在被攻击的程序中了,所要做的只是对代码传递一些参数。比如,攻击代码要求执行exec(/bin/sh),而在libc库中的代码执行exec(arg),其中arg 使一个指向一个字符串的指针参数,那么只要把传入的参数指针改为指向/bin/sh。

控制程序转移

所有的这些方法都是在寻求改变程序的执行流程,使之跳转到攻击代码。最基本的就是溢出一个没有边界检查或者其它弱点的缓冲区,这样就扰乱了程序的正常的执行顺序。通过溢出一个缓冲区,那就可以直接用暴力的方法改写相邻的程序空间而直接跳过了系统的检查。分类的基准是攻击者所寻求的缓冲区溢出的程序空间类型。原则上是可以任意的空间。实际上,许多的缓冲区溢出是用暴力的方法来寻求改变程序指针的。这类程序的不同之处就是程序空间的突破和内存空间的定位不同。

活动纪录

每当一个函数调用发生时,调用者会在堆栈中留下一个活动纪录,它包含了函数结束时返回的地址。通过溢出堆栈中的自动变量,使返回地址指向攻击代码。通过改变程序的返回地址,当函数调用结束时,程序就跳转到设定好的地址,而不是原先的地址。这类的缓冲区溢出被称为堆栈溢出攻击,是最常用的缓冲区溢出攻击方式。

函数指针

函数指针可以用来定位任何地址空间。
例如:void(*foo)() 声明了一个返回值为 void 的函数指针变量 foo。所以只需在任何空间内的函数指针附近找到一个能够溢出的缓冲区,然后溢出这个缓冲区来改变函数指针。在某一时刻,当程序通过函数指针调用函数时,程序的流程就背改变了。

代码植入和流程控

最简单和常见的缓冲区溢出攻击类型就是在一个字符串里综合了代码植入和活动纪录技术。定位一个可供溢出的自动变量,然后向程序传递一个很大的字符串,在引发缓冲区溢出,改变活动纪录的同时植入了代码。代码植入和缓冲区溢出不一定要在在一次动作内完成。可以在一个缓冲区内放置代码,这是不能溢出的缓冲区。然后通过溢出另外一个缓冲区来转移程序的指针。这种方法一般用来解决可供溢出的缓冲区不够大的情况。如果试图使用已经常驻的代码而不是从外部植入代码,通常必须把代码作为参数调用。举例来说,在 libc 中的部分代码段会执行 exec(something),其中 somthing 就是参数。然后使用缓冲区溢出改变程序的参数,然后利用另一个缓冲区溢出使程序指针指向 libc 中的特定的代码段。