ROP基本思路
1.存在栈溢出(如gets,read)至少能写入到执行至retn时的栈顶处
2.获取shell的函数选择(system,execve,syscall)
3.获取对应函数的地址(看导入表中有无对应函数,如果无,需要通过write或puts等函数泄露相对地址),泄露地址可以通过DynELF实现
4.在栈上布置调用函数所需要的非指针参数(入寄存器的参数通过多次执行含pop寄存器的gadgets实现)
5.指针参数如字符串“/bin/sh”可以采用直接搜索(在ELF中,已知相对地址可在libc中),或者在程序可写的数据段(.bss)中写入。
6.写出若干payload(泄漏地址+布置参数调用起shell函数),注意架构的区别,不能确定调用指令用gcc -S汇编.c文件测试
x64与x86 ROP的区别
首先,x64下的地址长度为8byte,而x86下为4byte
其次,x86中的read,write,system函数都是通过栈传递参数的,因此只要能在栈上写入参数即可。但是x64中则是通过rdx,rsi,rdi(edx,edi,esi)三个寄存器(或三个中的部分)传递参数,因此需要使用ROPgadget或pwntool中的ROP.find_gadget搜索pop相应寄存器的片段,通过栈上保存的参数写入寄存器再调用。
1 | read(fd:edi,*buf:rsi,len:edx) |
即前6个从左到右依次放入rdi,rsi,rdx,rcx,r8,r9,超出6个的参数从右向左放入栈中
执行保护机制
CANNARY:调用函数返回前检查堆栈是否被修改损坏
NX:数据内存页与可执行代码分开标记,在数据页上执行会抛出异常
PIE: 地址随机化,可以使得如libc中函数相对偏移改变
RELRO:设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对GOT(Global Offset Table)攻击。RELRO为” Partial RELRO”,说明我们对GOT表具有写权限。