緩衝區溢位 (Stack buffer overflow) 是傳統且常見的資訊安全攻擊手法. 主要透過程式漏洞造成輸入緩衝區溢位, 從而破壞原本程式執行、並取得系統的控制權.
而透過 buffer overflow 攻擊手法 [1] 以及攻擊範例 [2] 的相關資訊可瞭解如何使用 buffer overflow 來蓋掉 return address 內的資料以執行 shell code, 最終得到系統最高權限.
Stack buffer overflow 防護機制
可透過下列幾種方法來避免或降低 stack buffer overflow 造成的系統安全危害
- KALSR & ALSR (Address space Layout Randomization) [15][16]
- Executable Stack Protection [17]
- Position Independent Executables [18]
- Fortify Source [19]
- Stack Canaries [4][9][11][12]
√ Stack Canaries
stack canaries 又稱為 stack protector, 此方法會在程式中加入 “canary value". 之所以會稱之為金絲雀 (canary) 是因為早期煤礦工人常會在工作時發生急性一氧化碳(煤氣)中毒事件. 但以前沒有先進裝備來偵測一氧化碳(煤氣), 所以煤礦工人都會帶金絲雀 (canary) 進去礦坑工作. 原因是金絲雀對一氧化碳(煤氣)非常敏感, 些許煤氣就會讓金絲雀啼叫甚至死亡. 因此礦工會將金絲雀放在礦坑中作為是否需要逃生的依據.
同理可知, 若我們先在程式中加入 “canary value", 若 buffer overflow 異常情況發生, 則會修改 “canary value". 所以我們可透過檢查 “canary value" 是否遭修改來決定程式是否繼續往下執行或者中止結束.
1. User space stack protector
在 user space 底下可使用 toolchain 來達到 stack protector 的目的. 以 gcc 7.1 為例, 共支援 4 種類型
–fstack-protector
- 在使用到動態配置記憶體 (alloc) 或者 buffer > 8 bytes 的函數時加入以及檢查 canary 值
–fstack-protector-all
- 對所有 function 都加入以及檢查 canary 值
–fstack-protector-strong (gcc 版本需 >= 4.9)
- 除了 -fstack-protector 的條件外, 符合下列條件皆會加入以及檢查 canary 值
1. 若 local 變數位址用來賦值或者當作函式參數
2. local 變數為陣列類型, 或者 union 內含陣列
3. 以 register 類型宣告的 local 變數
–fstack-protector-explicit
- 只對以 __attribute__((stack_protect)) 宣告的 function 加入以及檢查 canary 值
> e.g. __attribute__((stack_protect)) test()
-fno-stack-protector
- 不啟用 stack protector
√ 運作原理剖析
以下列明顯有漏洞的程式為例
在 32-bit 作業系統環境下使用 gcc 7.1 的編譯結果如下:
note : 若在 64-bit 作業系統下則會差 8 bytes
由上表可知, 若啟用 stack protector 選項, 編譯器會做一些處理以至讓 binary 增加大小.
讓 binary 增加的部分, 勢必就是 Stack protector 施展魔法之處. 我們來看究竟在 binary 發生甚麼事
readelf -s hello-stack-protector
- 讀取 binary symbol table, 可以發現 binary 被 gcc 插入兩個 glibc 函式, 分別位於 [20]以及 [21] 用來處理 overflow 時的行為
那甚麼時候會執行 __stack_chk_fail_locak 呢? 我們可以使用 objdump 工具來反組譯 binary 以獲得更多訊息
objdump -d hello-stack-protector
- 我們可以看到, 裡面將 %gs:0x14 的值 (canary value) 放進暫存器, 然後存放到 address (ebp –0xc) 的地方, 以進行防護
- #60c 執行 strcpy 後, 會將 address (ebp –0xc) 的 canary值取出, 並和 %gs:0x14 裡的值做比較
- 若比較結果一樣 (jump equal) 則跳到 #62a, 如果canary的值不一致, 代表偵測到 overflow, 執行 #625 的__stack_chk_fail.
- %gs:0x14 的值為 kernel space所賦予之亂數, 詳情可參考 [22]
心得:
Security 和 Performance 往往是天秤的兩端, 設計時要好好考慮選用適當的防護手法. 不過一般而言, 都會建議預設使用 fstack-protector-strong 來編譯程式
References:
[1]: https://en.wikipedia.org/wiki/Stack_buffer_overflow#Exploiting_stack_buffer_overflows
[2]: https://dhavalkapil.com/blogs/Shellcode-Injection/
[3]: https://en.wikipedia.org/wiki/Buffer_overflow_protection
[4]: https://en.wikipedia.org/wiki/Stack_buffer_overflow#Stack_canaries
[5]: https://en.wikipedia.org/wiki/Sentinel_species#Historical_examples
[6]: https://www.win.tue.nl/~aeb/linux/hh/protection.html
[7]: https://www.phoronix.com/scan.php?page=news_item&px=KASLR-Default-Linux-4.12
[8]: https://my.oschina.net/macwe/blog/610357
[9]: https://hardenedlinux.github.io/2016/11/27/canary.html
[10]: https://gcc.gnu.org/onlinedocs/gcc-4.9.3/gcc/Optimize-Options.html
[11]: http://wiki.osdev.org/Stack_Smashing_Protector
[12]: https://lwn.net/Articles/584225/
[13]: https://gcc.gnu.org/ml/gcc-patches/2012-06/msg00974.html
[14]: http://www.madhur.co.in/blog/2011/08/06/protbufferoverflow.html
[15]: https://en.wikipedia.org/wiki/Address_space_layout_randomization
[16]: https://lwn.net/Articles/569635/
[17]: https://en.wikipedia.org/wiki/Executable_space_protection
[18]: https://en.wikipedia.org/wiki/Position-independent_code#Position-independent_executables
[19]: https://access.redhat.com/blogs/766093/posts/1976213
[20]: https://sourceware.org/git/?p=glibc.git;a=blob;f=debug/stack_chk_fail_local.c;h=eb0a759c4bc94d472504dea8759040c47f64efda;hb=HEAD
[21]: https://sourceware.org/git/?p=glibc.git;a=blob;f=debug/stack_chk_fail.c;h=9ab9bc7cebf7ed21a13af29c860ae683ea0d29e6;hb=HEAD
[22]: http://elixir.free-electrons.com/linux/latest/source/arch/x86/include/asm/stackprotector.h