我们以ciscn_2019_es_2为例来学习一下栈劫持
安全策略
[*] '/root/ctf/buuctf/pwn/ciscn_2019_es_2'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8046000)
分析
溢出点
sym.vul
函数存在两个溢出点,read函数向0x28大小的栈中读入0x30个字节。但问题在于可以操作的空间只有4个字节,无法传入一个完整的ROP链。
注意到题目提供了两个溢出,这个很值得玩味,猜测应该是第一个用来泄露,第二个用来劫持。
栈地址
如下图栈结构所示,注意到ebp的值0xfffd898
距离输入的aaaa
字符串的地址偏移是0x38。
00:0000│ esp 0xffffd850 ◂— 0x0
01:0004│ 0xffffd854 —▸ 0xffffd860 ◂— 'aaaa\n'
02:0008│ 0xffffd858 ◂— 0x30 /* '0' */
03:000c│ 0xffffd85c —▸ 0xf7fcfd60 (_IO_2_1_stdout_) ◂— 0xfbad2887
04:0010│ ecx 0xffffd860 ◂— 'aaaa\n'
05:0014│ 0xffffd864 ◂— 0xa /* '\n' */
06:0018│ 0xffffd868 ◂— 0x0
... ↓ 5 skipped
0c:0030│ 0xffffd880 —▸ 0x80486d8 ◂— push edi /* "Welcome, my friend. What's your name?" */
0d:0034│ 0xffffd884 —▸ 0xffffd944 —▸ 0xffffdac9 ◂— '/root/ctf/buuctf/pwn/ciscn_2019_es_2'
0e:0038│ ebp 0xffffd888 —▸ 0xffffd898 ◂— 0x0
因此可以利用第一组read-print泄露ebp的值,从而获得栈空间的地址。
payload1 = b'a' * 0x28
当然这里也可以利用其他的方式泄露栈空间地址,下面完整exp就是用的另一种方式,但是在输出的时候,有可能因为中间出现\x00
而失败。
栈劫持
首先直接粘贴了CTFwiki中关于栈劫持的
stack pivoting,正如它所描述的,该技巧就是劫持栈指针指向攻击者所能控制的内存处,然后再在相应的位置进行 ROP。一般来说,我们可能在以下情况需要使用 stack pivoting
- 可以控制的栈溢出的字节数较少,难以构造较长的 ROP 链
- 开启了 PIE 保护,栈地址未知,我们可以将栈劫持到已知的区域。
- 其它漏洞难以利用,我们需要进行转换,比如说将栈劫持到堆空间,从而在堆上写 rop 及进行堆漏洞利用
要劫持栈的话,需要控制栈指针esp的值
jmp esp ; ret
这个比较好理解,直接跳转到e