iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 4
1
Security

CTF 的三十道陰影系列 第 4

Day4: [Pwn] Buffer Overflow (CWE-120)

前言

接著幾天應該會陸續介紹六大系中比較簡單且經典的題目,讓大家可以對 CTF 有比較直觀的了解,接下來才會開始提升難度,預計可能 Day 10 之後就不太適合初學者閱讀了,也是我可能會開始斷更的日子...Orz

Buffer Overflow (CWE-120)

CWE-120: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')

Buffer Overflow (後簡稱 BOF) 根據 CWE 的定義是:

The program copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.

對於會寫 C 語言的人,應該看上面的敘述就可以了解何謂 BOF,C 語言在處理資料流時,必須事先宣告 buffer 來處理資料,需要精確了解要處理資料的大小避免發生 bug,但比較現代的程式語言在 buffer 不夠時會自動 extend buffer (e.g. 像是 C++ 的 string),即使不會自動 extend 時也會在 runtime 偵測到並拋出 exception,也因為這樣,近代的程式語言比較不容易發生 BOF 的問題

底下是一個很典型的 BOF 範例:

void foo(int size)
{
    char buf[0x20];
    read(0, buf, size);
    printf("buf: %s\n", buf);
}

int main(int argc, char *argv[])
{
    foo(atoi(argv[1]));
    return 0;
}

將上面的程式碼編譯成執行檔之後正常執行會得到這樣的結果:

dada@DADA-LAPTOP:/tmp$ echo -ne 'ggwp' | ./a.out 100
buf: ggwp

輸了了 buf: ggwp,跟我們的預期相符,但如果給比較長的 input 就會發生奇怪的現象,下面一共給了 41 個 a

dada@DADA-LAPTOP:/tmp$ echo -ne 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' | ./a.out 123
buf: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaau*

我們預期的 output 應該是 buf: aaaa....,但可以看到上面被我特別標成粗體的 u*,接在 41 個 a 後面,並不是我們預期的 output,這就是發生 BOF 症狀的一種,程式將 buf 後面不應該被存取的資料給印出來了,這是因為 printf()%s 的功能是將後面參數視為字串印出,C 語言預設對字串的結尾是 '\0' (null byte),但由於 read() 得到的 input 已經超過 buf 宣告的大小,並且中間沒有任何 null byte,因此會將後面原本不應該存取的資料視為字串的一部分而印出來

BOF 根據 buffer 在記憶體中儲存位置的不同,大致可以分為三個種類:

  • stack overflow
  • heap overflow
  • 其他 (bss overflow, data overflow)

三者的 root cause 基本上相同,都是因為在進行 buffer copy 時沒有檢查 input size,導致資料可能會超出 output buffer 而改到其他資料,但因為 stack, heap 和 bss 等的用途不同、存放資料也不同,導致發生 BOF 的利用方式和危害程度也會有差異

以前 stack overflow 是最好利用的,其次是 heap overflow,而發生在 bss 或 data 上的 overflow 要視能覆蓋掉什麼資料而定,不一定有辦法利用,但現今 stack 預設 compiler 有 stack canary 對 stack overflow 進行保護,heap 在 ptmalloc 的機制中也加了很多檢查,並且經常需要 leak 一些記憶體位置之後才方便利用,因此發生在 bss 或 data 段的 BOF 反而是最容易被忽略的

由於本次挑戰主旨是介紹 CTF 題目,因此這邊就不細講如何利用了,有興趣的人可以自行 google 相關教學,個人推薦 angelboy 的投影片

0x03: 32C3CTF 2015 PWN 200 readme

前面提到 stack overflow 在現今的有 stack canary 的保護之下,在現實環境下已經難以利用,除非像 CTF 題目刻意設計過,否則只有在 fork 出來的 process 下才有利用機會,但我當時在這題學到一招,可以在有 canary 的保護下,將 stack overflow 的漏洞變成可以 leak 任意 program data 的用法,解題的詳細經過可以參考我的 blog: https://ddaa.tw/32c3ctf_2015_pwn_200_readme.html

  • stack canary 會存放在程式的 Thread Local Storage (TLS) 上,此區段 fork 後會直接 copy 一份不會重新產生,因此只要 parent 不重啟就不會改變,有機會可以爆破 stack canary
    char flag[] = "NOT_THE_REAL_FLAG";

    int main()
    {
        char buf[0x100];
    
        puts("Hello!");
        printf("What's your name?");
        scanf("%s", buf);
        printf("Nice to meet you, %s.\n", buf);
        printf("Please overwrite the flag: ")
        scanf("%s", buf);
        memcpy(flag, buf, sizeof flag);
        puts("Thank you, bye!")
    
        return 0;
    }

這題的原始碼猜測大約是長這樣,問題在使用了 scanf("%s", buf) 的寫法,由於 scanf() 只有讀到換行 '\n'、空白 ' ' 等特殊字元才會停止,因此會發生 BOF

  • C 語言的入門教材卻經常使用這種寫法.....

但由於這題有 stack canary 的保護,即使一眼就看到漏洞也沒辦法簡單的疊 ROP 或跳 shellcode 拿 shell,加上這題很反常的直接把 flag 寫在 source code 裡面,當時就開始把方向想到要如何 leak data,經過一陣 google 之後搜尋到了一篇韓國 conference incognito 的投影片,裡面有用韓文描述如何透過偵測的 stack smash 的錯誤訊息來 leak 資料,具體作法如下:

  1. 透過第二次的 scanf 讓變數 flag 變成 LIBC_FATAL_STDERR=1 作為後面偽造環境變數的字串
  2. 輸入夠長的 input 發生 overflow,小心避開會讓 scanf 中斷的字元,在原本 stack 放環境變數的位置寫上變數 flag 的 address 0x600d20
  3. 在原本 stack 放 argv[0] 的位置寫上真正 flag 的位置 0x400d20
  4. 程式執行到 return 會因為偵測到 stack overflow,讓程式跳轉到 compiler 附加的 __stack_chk_fail() 進行錯誤處理
    • 環境變數已經被加入 LIBC_FATAL_STDERR=1,因此會將原本噴到 terminal 上的錯誤訊息改送到 stderr
    • argv[0] 已經被改成 flag 的位置,因此在印 stack smash 的錯誤訊息時,原本的程式名稱就會變成 flag 內容
      • __libc_message(2, "*** %s ***: %s terminated\n",msg, __libc_argv[0] ?: "<unknown>");
  5. 以下是執行結果:

Hello!
What's your name? Nice to meet you, .
Please overwrite the flag: Thank you, bye!
*** stack smashing detected ***: 32C3_ELF_caN_b3_pre7ty_we!rd... terminated


上一篇
Day3: [Crypto] Pseudo-Random Number Generator
下一篇
Day5: [Reverse] 淺談 reverse engineering
系列文
CTF 的三十道陰影31

尚未有邦友留言

立即登入留言