实验 5 缓冲区溢出实验
1.实验简介:实验目的是让学生了解缓冲区溢出脆弱点。缓冲区溢出是程序尝试向预先分配
的固定大小缓冲区写入数据超出边界。这个脆弱点能被恶意用户利用来改变程序的控制流程,
导致恶意代码的执行。这个脆弱点出现适应为数据的存储(如缓冲区)和控制存储(如返回
地址)混在一起:数据部分的溢出会影响程序的控制,因为溢出会改变返回地址。
本实验中,学生有带有缓冲区溢出脆弱点的程序;其任务是开发一种机制来利用脆弱点,并
最终获得根用户权限。除了攻击之外,学生也将学习到操作系统中包含的几种防御缓冲区溢
出的机制。学生需要评价这些机制是否好用,并解释原因。实验包括如下问题:缓冲区溢出
脆弱点和攻击、函数调用时栈的布局;shellcode;地址随机化;非执行栈;StackGuard。
2. 实验任务
2.1 关闭防御机制
在 ubuntu 虚拟机上完成实验。ubuntu 和其他 linux 系统中已经实现了几种缓冲区溢出攻击
的防御机制。为了简化实验,先将这些机制关闭。
地址空间随机化. ubuntu 和 linux 系统中使用地址随机化来随机化堆和栈的起始地址。这使
得准确猜测到地址很困难;地址猜测是缓冲区溢出的关键问题。本实验中使用如下命令关闭
该特征。
$ sudo sysctl -w kernel.randomize_va_space=0
StackGuard 保护机制. GCC 编译器实现了 StackGuard 机制来预防缓冲区溢出。有了这种机制,
缓冲区溢出就不再能工作。使用-fno-stack-protector 选项来关闭该机制。例如,在编译程序
example.c 时,将 StackGuard 关闭,可以使用下面命令:
$ gcc -fno-stack-protector example.c
Non-Executable Stack. Ubuntu 以前允许可执行栈,但是现在不再支持:程序的二进制镜像
(以及共享库)必须声明是否需要可执行栈,即,需要在程序头部打上标记。内核和动态链
接程序使用这个标记来决定是否让程序所用的栈可以执行或者不可以执行。这个标记是自动
由 gcc 来生成的,默认的是不可以执行的。要想设置,可以在编译程序时使用下面的选项:
For executable stack:
$ gcc -z execstack -o test test.c
For non-executable stack:
$ gcc -z noexecstack -o test test.c
/bin/sh 配置(仅限 Ubuntu 16.04). 在 Ubuntu12.04 和 16.04VM 中,/bin/sh 符号连接指向了
/bin/dash。但是 dash 程序在两个虚拟机中有所不同。在 ubuntu16.04 中,会组织 Set-UID 进
程的执行。如果 dash 发现自己在 Set-UID 进程中被执行,就会离开将有效用户 ID 变成进程
的实际用户 ID,即丢弃了特权。但是在 ubuntu12.04 中没有这个机制。
因为我们的受害程序是 Set-UID 程序,所以攻击要依赖/bin/sh,/bin/dash 下的机制会使得我
们的攻击更困难。所以我们将/bin/sh 链接到了其他的 shell 程序(后面的实验中也会给出
/bin/dash 也可以被 defeat 的办法)。在 ubuntu16.04VM 中,我们安装了 zsh 程序。使用下面
的链接命令/bin/sh 指向 zsh(在 12.04VM 中不必这么做)。
$ sudo rm /bin/sh; $ sudo ln -s /bin/zsh /bin/sh