实验一 Linux下缓冲区溢出
通过缓冲区溢出漏洞执行自己的代码
【实验代码】
【实验目的】
通过缓冲区溢出,使其执行not_called函数。
【实验原理及结果】
(1)编译程序(关闭掉栈保护):
(2)gdb调试程序
查看vulnerable_function函数汇编代码
我们可以看到该函数开辟了一个0x6c大小的缓冲区
| argument |
| return address |
| old %ebp> | <= %ebp
| 0x6c bytes of |
| … |
| buffer> |
| argument |
| address of buffer> | <= %esp
因此构造的buffer如下所示:
| not_called>的地址 |
| BBBB |
| 0x41414141 … |
| … (0x6c bytes of ‘A’s) |
| … 0x41414141 |
查看not_called函数的地址
因此,如下所示:
我们构造的缓冲区结构如下:
“A”*0x6c + “BBBB” + “\x44\x84\x04\x08”
缓冲区溢出漏洞改进版(1)
【实验代码】
实验代码同上
【实验目的】
第一次实验是通过缓冲区溢出执行自己的其他代码,这次主要是通过缓冲区溢出执行system函数,例如执行system(“/bin/sh”);
【实验原理及结果】
执行system函数,很明显要找到函数system以及字符串”/bin/sh”
(1) 找到system函数,函数地址如下所示:0x8048360
(2) 找到字符串/bin/sh
首先在main函数入口处设断点
找到system的地址
根据system找到字符串”/bin/sh”的地址
(3) 构造buffer
| 0x40197fb8 “/bin/sh” |
| 0x43434343 fake return address> |
| 0x8048360 address of system@plt> |
| 0x42424242 fake old %ebp> |
| 0x41414141 … |
| … (0x6c bytes of ‘A’s) |
| … 0x41414141 |
【运行结果】
缓冲区溢出漏洞(方法2)—利用pop-pop-return指令
【原理分析】
在这里我们使用pop-pop-return指令来执行代码
栈构造的格式如下:
| address of mov %eax, (%ecx)> |
| value to write> |
| address to write to> |
| address of pop %ecx; pop %eax; ret> |
【实验代码】
【实验目的】
本次试验的目的是先去call add_bin函数然后call add_sh函数,最后call exec_string函数
当我们call add_bin函数时,栈的结构如下:
| argument> |
| return address> |
首先我们要先构造参数0xdeadbeef返回地址是pop;ret。这样的话,函数就会从栈中弹出oxdeadbeef然后返回到下一个地址中。
| 0xdeadbeef |
| address of pop; ret> |
| address of add_bin> |
Add_sh函数需要两个参数,我们使用指令pop;pop;ret
| 0x0badf00d |
| 0xcafebabe |
| address of pop; pop; ret> |
| address of add_sh> |
因此我们总体构造的栈的结构是:
| address of exec_string> |
| 0x0badf00d |
| 0xcafebabe |
| address of pop; pop; ret> |
| address of add_sh> |
| 0xdeadbeef |
| address of pop; ret> |
| address of add_bin> |
| 0x42424242 (fake saved %ebp) |
| 0x41414141 … |
| … (0x6c bytes of ‘A’s) |
| … 0x41414141 |
【实验步骤】
1.查看pop;ret pop;pop;ret 以及三个函数的地址
Pop;ret的地址0x80484b7
Pop;pop;ret的地址为0x80484b6
函数add_bin:0x8048428
函数add_sh的地址0x804846e
函数exec_string的地址为0x8048414
(2)构造shellcode
【原理】
加载程序不再使用固定的基址加载,从而干扰 shellcode 定位的一种保护机制
在 VS2005 VS2008 中启用里链接/dynmicbase 链接选项可以开启 ASLR 保护,如图为 vs 中 ASLR 的编 译选项:
VS2008 VS2013 linker->Randomized Base Address 设置ASLR
ASLR 保护措施:
1. 映像随机化:
在 PE 文件映射到内存中时,对其加载的虚拟地址进行随机化处理,这个地址在系统启动 时确定,系统重启后这个地址会变化。但是虽然模块的加载基址变化了,但是各模块的入口点地址的低两位是不变的,也就是说映像随机化只是对加载基址的前两个字节做了随机处理。两次开机重启之后 PE 文件映射到内存中位置是不同的。
2.堆栈随机化
每个线程的堆栈基址都做了随机化处理,使得程序每次运行变量的地址都不相同。在程序运行时随机的选择堆栈的基址,与映像基址随机化不同的是堆栈的基址不是在程序启动 时候确定的,而是在打开程序的时候确定的,也就是说在同一个程序任意两次运行时的堆栈 基址都是不同的,进而各变量在内存中的位置也是不确定的。这种处理只能防止精准攻击, 但是由于跳板指令的使用使得程序不需要精准跳转,只需要跳到一个大概的位置即可。 如图,第一次运行程序时堆栈的位置如图:
两次运行该代码堆栈情况分别如下:
3.PEB 和 TEB 随机化
编写程序获取当前进程的 TEB 和 PEB,TEB 存放在 FS:0 和 FS:[0x18]处,PEB 放在 TEB
运行了几次,进程的 PEB,TEB 的地址没有什么变化,说明 ASLR 对 PEB(Process Environment Block,进程环境块)和 TEB(Thread Environment Block,线程环境块)的随机化并不好。
【攻击手段】
1.攻击未启用 ASLR 模块
在当前进程里面找到一个使用 ASLR 的模块,利用它里面的指令来做跳板,跳入到
shellcode 里面即可。
2.利用 Heap apray 技术定位内存地址
通过申请大量的内存,占领内存中的 0x0C0C0C0C 的位置,并在这些内存中放置 0x90 和
shellcode,最后控制程序转入 0x C0C0 C0C0 执行,在函数执行返回执行后就会转入我们申请 的内存空间中。
3.利用 Java applet heap spray 技术定位内存空间
4.用.NET 控件禁用 ASLR
5.利用部分覆盖进行定位内存地址 映像随机化只是对映像加载基址的强两个字节做随机化处理,我们可以选择覆盖这个地址的
最后一个字节;此外,ASLR 只是随机化了映像的加载基址,而没有对指令序列进行随机化,
也就是说指令的执行相对于基址的位置是相对不变的。
【利用部分覆盖进行定位内存地址】
3.1 实验环境
操作系统 Windows Vista SP0
DEP 状态 Option
编译器 Visual Studio 2008
优化选项 禁用优化选项
GS 选项 GS 关闭
DEP 选项 /NXCOMPAT.NO
Build 版本 Release 版本
3.2 实验代码
实验思路和代码的简要解释:
(1)为了直观地反映绕过 ASLR 的过程,本次编译的程序不启用 GS
(2)编译程序禁用 DEP
(3)Test 函数中存在典型的溢出漏洞,通过复制超长字符串可以覆盖函数返回地址。
(4)复制结束后,test 函数返回 tt 字符数组的首地址
(5)我们找到跳板指令 call eax,用它地址的后两个字节覆盖返回地址的后两个字节
3.3 实验步骤
(1)先将 she llcode 设置为 2 62 个字节的 0x90,并将 memcpy 函数中的复制长度设置为
262,用 od 加载,找到 call eax 对应的地址的后两个字节
(2)构造 shellcode
构造之后的 shellcode 代码如下:
3.4 运行之后实验结果:
【实验分析】
(1)main 函数,栈中的变化图下图所示:
发现 call eax 的地址的后两位为 141C
(2)Call test 单步进入 test 函数:
在 test 函数中,是一个典型的缓冲区溢出的代码,memcpy 将 26 2 字节的 shellcode 复制到 了栈中,由于栈开辟了 256 个字节的空间,这样的话,就会发生缓冲区溢出,多出来的 6 个字节就会淹没掉 ebp 以及返回地址的最后两个字节,这两个字节里面的内容如下。
很明显函数的返回地址就是 call eax 的地址。
所以在函数返回的时候代码会跳到 call eax 这条指令的地方,由于 eax 中存放的是 shellcode的首地址,这样我们就转到了 shellcode 里面并执行恶意代码,达到目的。