stackoverflow之stack-pivoting
前言
pwn很爽,一时pwn一时爽,时时pwn时时爽。
stackoverflow
stackoverflow的条件是:
- 输入点(可往栈上写入数据)
- 溢出点(存在危险函数,且写入的空间没有被良好控制)
basic
1 | #include <stdio.h> |
该binary满足stackoverflow的条件,调用危险函数gets,往栈上的s[12]写入数据,同时没有控制写入空间。
stackoverflow最基础的利用方式就是劫持EIP到特定的函数,从而改变程序的执行流程,例如在本题中劫持到success函数。
如何劫持EIP到success函数?填满缓冲区,然后填入success函数的地址。
如何计算出缓冲区大小?如何找到success函数的地址?
1 | objdump -d stackoverflow -M intel | grep -C 30 "<main>" |
这条命令是使用objdump将stackoverflow反编译成intel语法的汇编代码,同时过滤出来main函数的前后30行。
由此即可构造出来payload:
1 | payload = '\x90'*offset+success_addr |
在写exploit前,需要确定binary是32位还是64位。
所有的条件都已经具备,下面为exploit:
1 | from pwn import * |
stack-pivoting
stack-pivoting也叫堆栈旋转。
以X-CTF-Quals-2016-b0verfl0w 为例:
binary中没有后门函数,利用思路是将shellcode写入栈中,然后劫持EIP到shellcode地址。
那么shellcode写入到栈中哪里好呢?一个很好的位置就是[ebp-20h],即伪代码中的s位置。
当将shellcode写入到[ebp-20h]中后,接下来就是如何劫持EIP到[ebp-20h]地址,利用的想法是stack-pivoting。
下图为stack-pivoting的payload在内存中的分布:
这段payload是往内存中依次填充shellcode\ebp\jmp_esp_addr\asm(sub esp,offset;jmp esp)
,
当binary被触发执行后,控制流会在原栈帧的基础上,进行旋转执行,如下图所示:
stack-pivoting的实现关键在于jmp_esp_addr的寻找和offset的计算。
此处jmp_esp_addr的特点正如命名,一个可以直接跳转到esp的ROP gadgets的地址,可以利用ROPgadget工具寻找:
1 | ROPgadget --binary b0verfl0w --only 'jmp|ret' |
jmp_esp_addr=0x08048504。
而offset涉及到EIP是否可以旋转回到原ESP地址即shellcode的位置,故offset的计算应该为:
offset=len(shellcode)+len(ebp)+len(jmp_esp_addr)
然而由于可写入的地址空间为0x20=32个字节,故shellcode的长度必需要尽量精短,满足在32个字节以内,故offset的计算为:
offset=0x20+len(ebp)+len(jmp_esp_addr)=0x20+4+4=0x28(binary为32位)
由此,exploit为:
1 | from pwn import * |
参考
github-ctf-wiki