美文网首页
缓冲区溢出

缓冲区溢出

作者: 丿曰 | 来源:发表于2018-07-18 22:03 被阅读0次
    • 知识背景
    • 栈溢出及防护
    • 堆溢出及防护

    一、知识背景

    关于栈溢出的知识背景

    1. 典型操作系统的内存布局:

    典型操作系统的内存布局

    分段的意义:
    缓存命中率高、访问控制

    2. 进程的内存布局:

    进程的内存布局

    3. 堆栈的区别:

    地址增长方向

    heap - 堆 地址从小到大
    stack - 栈 地址从大到小

    支持及作用

    栈时具有硬件直接支持的,栈存储数据和控制流信息。
    堆是由操作系统的库函数予以支持的,堆存储数据。

    4. ESP与EIP:

    esp - 栈指针寄存器

    esp指向栈顶(存储栈顶的地址)
    push则esp减
    pop则esp增

    eip - 指令指针寄存器

    eip指向.test中存放的代码指令,指向的指令为即将被执行的指令。

    5. 栈的工作原理:

    栈的作用:为函数的调用和执行维护存储结构。

    函数调用时:存储数据
    函数返回时:恢复数据

    栈帧:调用函数将在栈上创建一个结构化空间,称为栈帧。

    锚点:函数被调用时的栈指针位置。可用于对函数的局部数据寻址、返回到调用者。

    call - return的实现:

    1)存储参数
    push arg2
    push arg1
    2)存储函数f返回后需要执行的指令地址RA(push RA)
    call f
    3)存储返回位置
    把ebp原来存储的值压栈,将esp此时的位置X存入ebp。
    push %ebp
    mov %esp %ebp
    4)执行函数f的指令
    X可帮助函数f的局部变量寻址。
    5)返回
    将ebp存储的值(即返回位置)告诉esp,并回复ebp原来的数据。此外,通过此时栈顶的数据RA,定位到下一条需要执行的指令。
    mov %ebp %esp
    pop %ebp
    retn

    6. 缓冲区:

    copy-on-write:

    资源的复制只有在需要写入的时候才进行,在此之前,只是以只读方式共享。

    很多时候,函数执行中需要局部分配较大的线性内存空间。


    函数执行分配空间
    增长方向

    栈增值方向为从高到低,缓冲区的增长从低到高。

    要求

    缓冲区应该足够大,写入数据之前应该先检查是否仍有剩余空间可用。

    关于堆溢出的知识背景

    1. 堆的创建和维护:

    通过malloc实现


    malloc

    2. 堆的工作原理:

    malloc创建的区域首先是堆的场地(Arena),进程申请堆空间时,malloc从场地中划分对应大小的区域供其使用。同一场地内可能存在多个堆段。

    注意:

    Main Arena没有heap_info
    有多个heap的Thread Arena只有一个malloc_state

    场地的数量限制 - 与cpu内核数量有关:

    32位系统:#_of_Arena = 2 * #_of_cores + 1
    64位系统:#_of_Arena = 8 * #_of_cores + 1

    堆块的形态:

    N表示此块是否属于main arean; M表示此块是否是mmap()创建的; P表示前块是否正在使用。


    堆块形态 链表结构
    显式链表结构bin:

    bin是记录free chunk的链表数据结构,在glibc中可分为fastbinsY和bins。
    fastbin主要用于高效的分配和回收比较小的内存块,采用LIFO形式的单链表结构。


    bin结构
    顶块top chunk:

    不属于任何bin。
    当前所有空闲块(无论哪种bin)全都尺寸不合时,由顶块应急。
    顶块比请求尺寸大——分割供给使用,剩余部分为新顶块。
    顶块比请求尺寸小——全堆无适合块,扩展堆/分配新堆。

    二、栈溢出及防护

    1. 主要原因:忽略了边界检查

    -> 开发者认为被操作对象合法
    -> 二进制代码底层细节复杂,容易使人忽略问题的存在
    -> 一些重要的遗产代码在最初的设计中存在此类缺陷

    2. 现有的栈溢出防护技术

    数据执行保护(W⊕X,NX-bit):

    机制:
    对内存页面增加一个标识比特,使它要么可改写,要么可执行
    缺陷:
    只要不试图执行数据区内容,则既不能阻止溢出,又不能发现溢出。

    栈帧上的“金丝雀”:

    机制:
    栈上设置检查点,检查点数值异常,则代表栈溢出发生。
    缺陷:
    开销大,对遗产代码不起作用。

    影子栈:

    机制:
    构造一个额外的动态数据区域,称为影子站。当call发生时,在返回地址入栈的同时,也将其写入影子堆栈。每当return发生时,在弹出并转向返回地址前,将其与影子堆栈的栈顶比较。
    缺陷:
    开销大,无法保护遗产代码。

    分离的控制/数据栈:

    机制:
    修改操作系统的内存布局,分配两个栈空间,分别用于控制参数和数据的存储。
    缺陷:
    无法保护遗产代码,不适用于完全由汇编语言构成的函数。

    三、堆溢出及防护

    1. 与栈溢出的区别

    溢出方向等于堆增长方向。首先破坏(虚拟地址意义上的)下一个堆块的构造。

    2. linux上的典型堆溢出利用方式

    • 攻击fastbin
    • 攻击unlink
    攻击fastbin

    基于fastbin块LIFO的特点,我们可以先申请,然后释放,再申请就可以得到原来地址的块。
    但是这不能满足我们的需求,我们需要将堆分配在可控地址。
    我们可以通过堆溢出更改已经申请块的fd,使其指向我们可控的地址,并且在可控地址上伪造假的fastbin结构。
    释放,再申请两次,第2次就可以得到分配在可控地址上的块。(覆盖fd)


    攻击fastbin
    攻击unlink

    unlink攻击技术就是利用”glibc malloc”的内存回收机制,将second chunk给unlink掉,并且,在unlink的过程中使用shellcode地址覆盖掉free函数(或其他函数也行)的GOT表项。这样当程序后续调用free函数的时候(如上面代码[5]),就转而执行shellcode了。显然,核心就是理解glibc malloc的free机制。

    一旦涉及到free内存,那么就意味着有新的chunk由allocated状态变成了free状态,此时glibc malloc就需要进行合并操作——向前以及(或)向后合并。这里所谓向前向后的概念如下:将previous free chunk合并到当前free chunk,叫做向后合并;将后面的free chunk合并到当前free chunk,叫做向前合并。


    攻击思路 攻击思路

    3. windows堆溢出攻击的主要形式

    • 利用向量化异常处理(VEH)
    • 利用系统默认异常处理函数(UEF)
    • Heap spray
    • Bitmap Flipping攻击
    • Bitmap XOR攻击
    • Heap Cache攻击

    4. 堆溢出防御

    堆依靠系统库实现其维护,因此,堆保护 = 系统库升级。

    针对unlink的保护:

    Double Free检测
    next size非法检测
    双链表冲突检测

    相关文章

      网友评论

          本文标题:缓冲区溢出

          本文链接:https://www.haomeiwen.com/subject/neyapftx.html