在基础的软件安全实验中,缓冲区溢出是一个基础而又经典的问题。最基本的缓冲区溢出即通过合理的构造输入数据,使得输入数据量超过原始缓冲区的大小,从而覆盖数据输入缓冲区之外的数据,达到诸如修改函数返回地址等目的。但随着操作系统和编译器针对缓冲区溢出问题引入防护机制,初学者想要由简入繁的学习和实践缓冲区溢出的原理变得困难。在 Linux 环境下,用户可以通过设置编译和系统环境来去除某些防护措施,从而方便的完成某些简单的缓冲区溢出实验。

1.关闭SSP( Stack Smashing Protector )

  实现原理

  SSP 是 gcc 提供的针对栈上缓冲区溢出提供检查的机制。典型的缓冲区溢出攻击会通过构造输入数据覆盖缓冲区外的数据,实现一定的溢出效果,如修改函数返回地址等。启用 SSP 机制后,在编译器生成的代码中,对于那些存在缓冲区的函数,函数开始时会在对应栈帧中入栈一个随机值( canary ),在函数调用结束准备返回时,编译器生成的代码会检查上述引入的随机值是否发生了变化,若发生变化则说明出现了缓冲区溢出,控制流会转移至对应的处理函数处。

  以下列代码为例进行说明

    void simpleCopy( char *src )
{
  char buff[10];
  strcpy( buff , src );
}

  上述代码将缓冲区 src 中的数据简单的拷贝至 buff 数组中,而没有考虑 src 中的数据量是否超出了 buff 数组的容纳能力。

  使用 gdb 查看得到 simpleCopy 的汇编如图所示。

  

  在开启了 SSP 机制的 gcc 生成的代码中,函数开始数据处理之前,将 gs:0x14 处的值存放在了 %ebp - 12 处,在函数返回时,对 %ebp - 12 处的值进行检查,若此时值不等于 gs:0x14 处的值,则说明存在数据的覆盖和修改,程序会转入 __stack_chk_fail 执行。通过 simpleCopy("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")调用函数的结果如下图所示。上述 simpleCopy 函数中,缓冲区 buff 首地址为 %ebp - 22 , 而引入的 canary 存放在 %ebp - 12 处,当缓冲区存在数据溢出时,会修改 canary 的值,从而在函数返回时被检测到。

    

  选项设置

  gcc的编译设置中,默认设置SSP机制是有效状态,用户可在编译时加入 -fno-stack-protector 参数禁用SSP机制。 

    -fstack-protector    //编译时启用SSP机制
-fno-stack-protector //编译时禁用SSP机制

2.关闭DEP( Data Execution Prevention )

  在某些栈缓冲区数据溢出实践中,会将构造好的机器指令序列通过输入写入位于栈上的输入缓冲区中,同时,构造数据覆盖位于数据缓冲区外的函数调用的返回地址,将其值修改为缓冲区中构造好的机器指令序列的地址,这样当函数调用返回时,程序控制流会跳转至输入缓冲区中的构造指令序列,从而实现执行流的修改和劫持。而现代编译器中,为防止用户通过栈上的缓冲区构造数据,使用了DEP机制,即限制内存的属性,使得某些可写内存不可执行( 如栈 )或可执行内存不可写( 如.text段),从而达到防护的效果。

  使用 gcc 进行编译时,可通过 -z execstack 参数,使得最终生成的程序中栈内存段具备可执行权限。

    -z execstack    //设置栈内存段具备可执行权限

  在程序运行时,可通过查看 cat /proc/pid/maps 文件查看 pid 所对应的进程的内存映射情况,其中包括对进程的段的属性描述。

 3.关闭ASLR( address space layout randomization )

  在基础栈缓冲区溢出实践中,一个重要的步骤为定位某些目标的内存映射地址,如最简单的 shellcode 需定位注入栈上的构造指令序列的地址( 从而修改函数返回地址指向该指令序列),ret2libc 方法则需要攻击者定位位于内存中的标准库函数的内存映射地址等。ASLR ,即内存布局随机化机制由操作系统实现,其主要被划分为映像随机化、栈随机化和堆随机化这几类,分别针对程序的加载基地址、栈基址和堆基址进行随机化。

  设计原理

  已经编译完成的程序的各个段在内存中的加载基位地址是固定的,也就是说程序运行时的内存映射情况是固定的,攻击者可以通过多次调试运行程序,获得他们所需的目标地址。在编译好的 ELF 文件中,段头部表描述了程序需加载入内存的各个段的基地址,可通过 readelf -h filename 对其进行查看。对于运行中的程序,可以通过 cat /proc/pid/maps 查看 pid 对应的进程的内存映射情况,包括栈、堆的基地址等。

        objdump -h hello    //查看可执行文件hello的段头部表
cat /proc/pid/maps //pid为进程号,查看pid对应进程的内存映射情况

  (1)通过 objdump -h hello 查看该 ELF 文件的.text 加载基地址为 0x08048370,而.rodata段的加载基地址为 0x08048558。

    

  (2)通过 cat /proc/$$/maps 查看 shell 的内存映射情况(64位Ubuntu),可以看到 shell 进程的 stack 段的内存范围以及其属性为可读可写但不可执行,p 表示该虚拟内存段为私有的( private ),s 表示该虚拟内存段为共享的( shared )。

    

  在过去的编译环境中,程序的数据段包括.text、.bss、.rodata 段加载进内存的基地址在编译时即已确定,程序运行时进程中 stack 和 heap 的起始地址也总是固定的,这就使得攻击者可以较容易的定位内存中目标的地址,从而进行攻击尝试。在引入 ALSR 机制后,对于程序的某个特定的段( .text 、stack 、heap ),操作系统会在加载时自其原始的起始基地址处加入一个随机大小的“填充”,对应的程序数据总是自“填充”区域后开始,由于每次使用的“填充”的大小并不固定,同一程序多次运行时的内存布局会发生变化,从而使得攻击者较难通过固定的地址去访问其所需要的目标。

  选项设置

  用户可通过cat /proc/sys/kernel/randomize_va_space 查看当前 ALSR 机制的运行情况。其中,为 0 表示 ALSR 机制未启用,为 1 表示 ALSR 机制会随机化 stack、vdso和 mmap 的起始基地址,为 2 表示除对上述目标进行随机化外还会对堆基地址进行随机化。用户可通过 echo 0 > /proc/sys/kernel/randomize_va_space 设置关闭 ALSR

     cat /proc/sys/kernel/randomize_va_space       //查看 ALSR 机制的运行情况
echo > /proc/sys/kernel/randomize_va_space //设置关闭 ALSR 机制,需要 root 用户进行操作

  上述对 /proc/sys/kernel/randomize_va_space 的操作为针对系统的全局设置,需要 root 权限且存在弊端,用户可通过以下命令将当前终端 /bin/bash 的 ALSR 机制关闭,则通过该 shell 运行的程序均不会启动 ALSR 机制。该终端关闭后上述设置即失效。

   setarch `uname -m` -R /bin/bash

4.通过 gdb 获得目标地址

  基础的缓冲区溢出实践通常需要确定运行状态下程序中的某些地址,如需要确定输入缓冲区的起始地址从而获得注入缓冲区中的机器指令的地址等。在 Linux 环境下,可通过 gdb 对程序进行动态调试,从而获得程序运行状态下的信息( 关闭 ALSR 机制后效果更好 ),基础的 gdb 操作可参见笔者的文章Linux下编辑、编译、调试命令总结——gcc和gdb描述。使用 gdb 可以方便的获取程序动态运行状态下的信息,但通过 gdb 动态调试获取的诸如缓冲区的起始地址等信息可能与程序实际运行时的信息并不相同,从而影响缓冲区溢出实践的效果。关于保证通过 gdb 动态调试程序获得的局部变量的地址与直接运行该程序时的地址一致的问题,笔者另外通过博文针对 Linux 环境下 gdb 动态调试获取的局部变量地址与直接运行程序时不一致问题的解决方案进行描述。

5.总结

  为了完成基础的缓冲区溢出实践,用户可通过 gcc 的编译选项 -fno-stack-protector 关闭 SSP 保护机制,通过 -z execstack 选项使得生成的可执行程序的栈可执行,通过 setarch `uname -m` -R /bin/bash 设置关闭当前终端中运行程序的 ALSR 机制,并通过 gdb 动态调试获取某些所需要的程序局部变量的信息。

6.参考资料

  1.Stack Smashing Protector - OSDev Wiki

  2.protect against buffer overflow exploits

  3.Address space layout randomization - Wiki

使用Linux进行缓冲区溢出实验的配置记录的更多相关文章

  1. Kali学习笔记33:Linux系统缓冲区溢出实验

    之前做过一个Windows应用SLmail的缓冲区溢出的实验 这次来做一个Linux平台的缓冲区溢出实验: 缓冲区溢出是什么? 学过汇编的应该知道,当缓冲区边界限制不严格时,由于变量传入畸形数据或程序 ...

  2. 2018-2019-2 20165232《网络对抗技术》Exp1 缓冲区溢出实验

    2018-2019-2 20165232<网络对抗技术>Exp1 缓冲区溢出实验 实验点1:逆向及Bof基础实践 实践任务 用一个pwn1文件. 该程序正常执行流程是:main调用foo函 ...

  3. 2017-2018-2 20179215《网络攻防实践》seed缓冲区溢出实验

    seed缓冲区溢出实验 有漏洞的程序: /* stack.c */ /* This program has a buffer overflow vulnerability. */ /* Our tas ...

  4. SEED缓冲区溢出实验笔记

    缓冲区溢出实验(Linux 32位) 参考教程与材料:http://www.cis.syr.edu/~wedu/seed/Labs_12.04/Software/Buffer_Overflow/ (本 ...

  5. 20191310Lee_yellow缓冲区溢出实验

    缓冲区溢出实验 1.什么是缓冲区溢出 ​ 缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况.这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代码的任意片段.这一漏洞的出现是由于数据 ...

  6. Linux下缓冲区溢出攻击的原理及对策(转载)

    前言 从逻辑上讲进程的堆栈是由多个堆栈帧构成的,其中每个堆栈帧都对应一个函数调用.当函数调用发生时,新的堆栈帧被压入堆栈:当函数返回时,相应的堆栈帧从堆栈中弹出.尽管堆栈帧结构的引入为在高级语言中实现 ...

  7. 2018-2019-2 20165225《网络对抗技术》Exp1 缓冲区溢出实验

    2018-2019-2 20165225<网络对抗技术>Exp1 缓冲区溢出实验 声明 虽然老师在邮箱中要求要把虚拟机名改为个人名字缩写,但是我的kali好像不是很听话...重启数次也没用 ...

  8. Kali学习笔记21:缓冲区溢出实验(漏洞发现)

    上一篇文章,我已经做好了缓冲区溢出实验的准备工作: https://www.cnblogs.com/xuyiqing/p/9835561.html 下面就是Kali虚拟机对缓冲区溢出的测试: 已经知道 ...

  9. Linux下缓冲区溢出攻击的原理及对策

    前言 从逻辑上讲进程的堆栈是由多个堆栈帧构成的,其中每个堆栈帧都对应一个函数调用.当函数调用发生时,新的堆栈 帧被压入堆栈:当函数返回时,相应的堆栈帧从堆栈中弹出.尽管堆栈帧结构的引入为在高级语言中实 ...

随机推荐

  1. BG.Hive - part2

    1. 将mysql的订单数据导入hive的分区表(桶.倾斜)[partition,bucket,skew] a> 在Hive中新建分区表 CREATE TABLE IF NOT EXISTS H ...

  2. [转] ASP.NET MVC 模型绑定的功能和问题

    摘要:本文将与你深入探究 ASP.NET MVC 模型绑定子系统的核心部分,展示模型绑定框架的每一层并提供扩展模型绑定逻辑以满足应用程序需求的各种方法. 同时,你还会看到一些经常被忽视的模型绑定技术, ...

  3. iOS调用系统页面中文显示

    在开发的过程中,我们会接入很多的sdk,比如相机,相册,是否允许获取位置等,大多数的情况下是默认显示英文, 在plist文件里面添加一个key就可以了,如下图: key:Localization na ...

  4. SpringCloud Eureka(注册中心集群)

    多个注册中心,其实用不同的配置对应 不同的端口号注册就行了. 注册中心自己也是个服务,看看之前的单个注册中心是怎么样的呢? server: port: 8888 # 服务端口eureka: insta ...

  5. logback和slf4j的使用之logger使用

    原文:https://blog.csdn.net/cw_hello1/article/details/51923814 一.logger标签描述:(了解logger标签之前先看看两个重要概念) 1.主 ...

  6. spring boot入门笔记(四) - 多环境配置、加载顺序、静态资源映射

    1.多环境配置 先描述下以前的开发流程:从SVN把项目下载到本地,各种修改配置文件,启动成功:完成功能后上传到公司的测试服务器,修改各种配置文件,启动成功:最后到上线的日子里,把新功能中涉及到的文件打 ...

  7. 不要在对抽象类接口abstract virtual什么的混淆不清了

    (最近带一个新手入门 讲这边老费力了.) 接口和抽象类不过都是类的抽象罢了,这是他们的本质,只不过人们的思维角度不同而已,一个偏向为行为契约上的抽象(接口) 一个更像大自然中的繁衍关系(抽象类). 直 ...

  8. js判断移动端页面按home键切换到桌面事件

    ---恢复内容开始--- 原理就是通过页面标签切换事件(visibilitychange)来判断,亦可用户移动端桌面和app切换. 先看代码: var hiddenProperty = 'hidden ...

  9. js二分查找树实现

    function BinaryTree() { var Node = function(key) { this.key = key; this.left = null; this.right = nu ...

  10. 移动端 vconsole 的使用

    在微信和app里  我们没有办法使用谷歌提供的开发者工具,可以借助 vconsole 使用步骤如下 1.安装 npm install vconsole 2.app.js里引入 import VCons ...