菜鸡刚学rop,总结下,算是对蒸米大佬这篇文章最后一个样例的一点解释说明。
笔记中的程序源代码来自 蒸米大佬的x86一步一步学rop http://www.vuln.cn/6645
计算溢出点的位置的脚本可以在大佬的github上下载(其实从IDA上也可以计算出来溢出点)
https://github.com/zhengmin1989/ROP_STEP_BY_STEP
预备知识:
了解动态链接中PLT表和GOT表。
ASLR的开启无法通过checksec来检测,他的开启与系统有关。
ASLR只针对动态库基址的中间位数进行随机化,后三位并不会变。
ASLR不会随机化本身程序的基址。
在不开启PIE的情况下,可以使用ret2libc来绕过NX和ASLR保护。
下面是利用的思路:
这个思路是通解的思路,即不知道目标程序使用的动态库的版本。下面的样例题目,是已知目标程序使用的动态库版本。
样例的源代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void vulnerable_function() {
char buf[128];
read(STDIN_FILENO, buf, 256);
}
int main(int argc, char** argv) {
vulnerable_function();
write(STDOUT_FILENO, "Hello, World\n", 13);
}
编译时关闭canary和PIE防护,打开了ASLR和NX保护。
此时动态库的基址是会发生变化的:
本题的利用思路就是用write函数,打印出write函数对应的got表里的内容。
虽然write函数的地址并不是固定的,但是程序本身使用过这个函数,所以PLT表里一定有这个函数,PLT表又属于本身程序的代码段,在没有开启PIE的情况下,write函数对应的PLT表项的地址是确定的。
则可以将返回地址覆盖为write函数对应的PLT表的地址,参数布置为,1,write函数对应的got表项的地址,4。
再将write函数的返回地址布置为vulnerable_function 这个函数的地址,执行完write函数后,返回到vulnerable_function函数,进行二次溢出,获得shell。
其中1是标准输出(即从终端显示输出结果),4是输出的长度。
覆盖前堆栈图如下:
覆盖后堆栈的情况如下:
打印出write 的地址后,计算出system函数的地址,然后利用二次溢出,覆盖返回地址为system函数地址,布置参数为/bin/bash 字符串的首地址。
利用脚本如下: