栈溢出实战(34)
栈溢出原理
由于C语言对数组引用不做任何边界检查 ,从而导致缓冲区溢出。
栈没有保护,指针可以通过传入的参数的大小,一直基于基地址往上指。
缓冲区溢出:
- 栈溢出
- 栈上保存着局部变量和一些状态信息(寄存器值,返回地址…)
- 发生了溢出就可以做到随意的该这些状态信息。并且攻击者可以通过覆写返回地址来执行代码,利用方法包括shellcode注入,ret2text,ret2libc ,ROP…
- 堆溢出
实现
- 首先你得能在栈上写数据
- 写入的数据大小没有被检测或者未被检测到
1 |
|
gcc 编译指令中,-m32 指的是生成 32 位程序; -fno-stack-protector 指的是不开启堆栈溢出保护,即不生成 canary。 此外,为了更加方便地介绍栈溢出的基本利用方式,这里还需要关闭 PIE(PositionIndependent Executable),避免加载基址被打乱。不同 gcc 版本对于 PIE 的默认配置不同,我们可以使用命令gcc -v查看 gcc 默认的开关情况。如果含有–enable-default-pie参数则代表 PIE 默认已开启,
需要在编译指令中添加参数-no-pie。通过漏洞达到执行success函数
1 | gcc -m32 -fno-stack-protector test.c |
提到编译时的 PIE 保护,Linux 平台下还有地址空间分布随机化(ASLR)的机制。简单来说即使可执行文件开启了 PIE 保护,还需要系统开启 ASLR 才会真正打乱基址,否则程序运行时依旧会在加载一个固定的基址上(不过和 No PIE 时基址不同)。我们可以通过修改 /proc/sys/kernel/randomize_va_space来控制 ASLR 启动与否,具体的选项有
0,关闭 ASLR,没有随机化。栈、堆、.so 的基地址每次都相同。
1,普通的 ASLR。栈基地址、mmap 基地址、.so 加载基地址都将被随机化,但是堆基地址没有随机化。
2,增强的 ASLR,在 1 的基础上,增加了堆基地址随机化。
我们可以使用echo 0 > /proc/sys/kernel/randomize_va_space关闭 Linux 系统的 ASLR,类似的,也可以配置相应的参数。
为了降低后续漏洞利用复杂度,我们这里关闭 ASLR,在编译时关闭 PIE。当然读者也可以尝试 ASLR、PIE 开关的不同组合,配合 IDA 及其动态调试功能观察程序地址变化情况(在 ASLR 关闭、PIE 开启时也可以攻击成功)
可以用checksec 来检测一下
我们用IDA来看看
1 | int vulnerable() |
发现这个ebp的长度是0x14;
这里bp的长度是0x20
那么栈的结果就是:
并且,我们可以通过 IDA 获得 success 的地址,其地址为 0x0804843B。
1 | .text:0804843B success proc near |
那么如果我们读取的字符串为
1 | 0x14*'a'+'bbbb'+success_addr |
那么,由于 gets 会读到回车才算结束,所以我们可以直接读取所有的字符串,并且将 saved ebp 覆盖为 bbbb,将 retaddr 覆盖为 success_addr,即,此时的栈结构为
但是需要注意的是,由于在计算机内存中,每个值都是按照字节存储的。一般情况下都是采用小端存储,即 0x0804843B 在内存中的形式是 \x3b\x84\x04\x08
但是,我们又不能直接在终端将这些字符给输入进去,在终端输入的时候 \,x 等也算一个单独的字符。。所以我们需要想办法将 \x3b 作为一个字符输入进去。那么此时我们就需要使用一波 pwntools 了(关于如何安装以及基本用法,请自行 github),这里利用 pwntools 的代码如下:
1 | ##coding=utf8 |