Pwnable.tw-3x17
Author: 堇姬Naup
分析
一樣IDA開逆
這題把debug symbol全部拔掉,我們先定位main在哪裡
start
1 | // positive sp value has been detected, the output may be wrong! |
實際去run這隻binary會看到他印出來的一些字,用這些字去定位
sub_401B6D() 是 main
先修復變數
main
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
簡單來說讀一個address並讀資料寫到該address,可以任意寫0x18
回到start
他call了一個function
看了一下,有__libc_start_main的特徵
1 | .text:0000000000401A50 xor ebp, ebp |
__libc_start_main他呼叫的架構長這樣
https://imaddabbura.github.io/posts/c/program-startup-notes.html
1 | __libc_start_main (int (*main) (int, char **, char **), |
對比我們的IDA結果
value | 意義 |
---|---|
v4 | argc |
sub_4028D0 | init function |
sub_402960 | fini function |
1 | _libc_start_main( |
init function
1 | __int64 __fastcall init_f(unsigned int a1, __int64 a2, __int64 a3) |
fini function
1 | __int64 fini_func() |
fini array有兩個值
1 | pwndbg> x/10xg 0x4b40f0 |
v0是2
理論上它會
libc_start_main -> libc_csu_init -> init ->main -> fini_function -> fini_array[1] -> fini_array[0]
fini array在 0x4B40F0
在可寫段,如果我可以改寫這段,跳回main,就可以有無限制的任意寫
我將 fini_arr1 -> fini_arr0 原有的值改成
main -> fini function -> main -> fini function -> …
這樣就有無限任意寫了
1 | from pwn import * |
他是static,代表他有gadget可以寫,堆ROP
gadget | address | value |
---|---|---|
/bin/sh | 0x4b9600 | none |
pop rax ; ret | 0x41e4af | 0x3b |
pop rsi ; ret | 0x406c30 | 0 |
pop rdi ; ret | 0x401696 | 0x4b9600 |
pop rdx ; ret | 0x446e35 | 0 |
syscall | 0x4022b4 | none |
1 | from pwn import * |
當我做到這樣,突然發現因為我沒辦法寫stack,所以我是找隨便一塊記憶體來放ROP,想要跳過去,但是這樣我就必須把rsp搬過去
這邊可以用stack migration
1 | mov rsp, rbp |
藉由將fini array的地方寫成leave+ret
就可以了
前提是將rbp設為ROP位置-0x8
那我究竟要怎麼控rbp呢?
這邊觀察到,他在call fini array內容前,rbp值是0x4b40f0
所以我們將ROPgadget寫在0x4b4100
不需要控rbp,直接leave後ret兩次就會跳上去(為了不要蓋到fini array內容所以這邊往後蓋0x10)
所以最後蓋
leave;ret + ret
因為執行順序是
現在在main,所以接下來執行fini array 1
之後執行fini array 0
執行完就RCE
1 |
|
(150分的題目體感比排tcache tear的heap還要複雜很多)