[pwn基礎]Linux安全機制
[pwn基礎]Linux安全機制
Canary(棧溢出保護)
Canary:(取名自地下煤礦的金絲雀,因為它能夠比曠工更早的發現煤氣泄漏,有預警的作用)
,是一種用於對抗棧溢出攻擊
的技術,即SSP安全機制
,有時候也叫做Stack cookies
。
Canary的值是棧
上的一個隨機數
,在程式啟動時隨機生成並且保存在比函數返回地址
更低的位置。(看過棧溢出文章的應該都知道,我們棧溢出就是為了覆蓋返回地址),由於我們覆蓋到返回地址,那麼不可避免的會覆蓋到這個隨機數(Cannary),程式在函數返回前,檢查下Cannary是否被覆蓋從而判斷是否被棧溢出,從而達到保護棧的目的。
開啟關閉Cannary
#默認情況下是會開啟Canary保護的
#禁用棧保護
gcc -fno-stack-protector cannary.c -o cannary
#開啟局部保護(只保護局部變數中含有char數組的函數)
gcc -fstack-protector cannary.c -o cannary
#開啟全局保護
gcc -fstack-protector-all cannary.c -o cannary
#命令速記
stack-protector(棧保護)
-f(開啟)
-fno(關閉)
-all(全部)
Canary的種類
Cannary通常可以分為3中類型,terminator、random、random XOR
,具體的實現有StackGuard、StackShield、Propoliced等
。
Terminator canaries(終結者金絲雀)
大多數緩衝區溢出攻擊都基於某些字元串操作,比如說strcpy
,這些操作基本上以字元串終止符結束\x00
。
終止符金絲雀包含 NULL(0x00)、CR(0x0d)、LF(0x0a)和 EOF(0xff)
這四個字元,這四個字元應終止大多數字元串操作,從而使溢出嘗試無害。
這可以防止使用 strcpy() 和其他方法的攻擊,這些方法在複製空字元時返回。
繞過方法:
攻擊者可以使用其已知值覆蓋金絲雀,並使用特製值覆蓋返回地址,從而繞過這種類型的保護,從而導致程式碼執行。
當使用非字元串函數來複制緩衝區並且緩衝區內容和緩衝區長度都受攻擊者控制時,可能會發生這種情況。
Random cannaries(隨機金絲雀)
隨機金絲雀是在程式執行時隨機選擇的。
使用此方法,攻擊者無法在程式啟動之前通過搜索可執行映像來了解金絲雀值。隨機值取自 /dev/urandom(如果可用),如果不支援 /dev/urandom,則通過hash一天中的時間來創建。這種隨機性足以阻止大多數預測嘗試。
繞過方法:
需要配合資訊泄漏漏洞
如果應用程式中存在可用於讀取金絲雀值的資訊泄漏漏洞,則可以繞過這種保護。
Random XOR cannaries(隨機異或金絲雀)
隨機異或金絲雀是使用全部或部分控制數據(幀指針+返回地址等)進行異或加干擾的隨機金絲雀。
這樣,一旦金絲雀或控制數據被破壞,金絲雀值就是錯誤的,它將導致程式立即終止。
繞過方式總結:
泄露記憶體中的canary,如通過格式化字元串漏洞列印出來
one-by-one爆破,但是一般是多執行緒的程式,產生新執行緒後canary不變才行。最高位為00。
劫持_stack_chk_fail函數,canary驗證失敗會進行該函數,__stack_chk_fail 函數是一個普通的延遲綁定函數,可以通過修改 GOT 表劫持這個函數。
覆蓋執行緒局部存儲TLS中的canary,溢出尺寸比較大可以用。同時修改棧上的canary和TLS中的canary.
NX(No-eXecute)
No-eXecute(NX)表示不可執行
,其原理是將數據所在的記憶體頁標識為不可執行。
相當於Windows平台上的DEP(數據執行保護),NX的實現由軟體和硬體共同完成
- 硬體層:利用CPU的NX位,對相應頁表項中的第63位進行設置
- NX=1,不可知性
- NX=0,可執行
- 軟體層面:需要作業系統支援NX以配置頁表,涉及相關API
- Windows:
VirtualAlloc
、VirtualProtect
等 - Linux:
mmap
、mprotect
等
- Windows:
if ${readelf} -l "${1}" 2>/dev/null | grep -q 'GNU_STACK'; then
if ${readelf} -l "${1}" 2>/dev/null | grep 'GNU_STACK' | grep -q 'RWE'; then
echo_message '\033[31mNX disabled\033[m ' 'NX disabled,' ' nx="no"' '"nx":"no",'
else
echo_message '\033[32mNX enabled \033[m ' 'NX enabled,' ' nx="yes"' '"nx":"yes",'
fi
else
echo_message '\033[31mNX disabled\033[m ' 'NX disabled,' ' nx="no"' '"nx":"no",'
fi
#判斷是否有GNU_STACK,繼續判斷是否有RWE許可權,否則就是開啟了NX保護.
繞過方式:
在Linx中,當裝載器將程式載入進記憶體空間後,將程式的.text節
標記為可執行,而其餘的數據段.data、.bss、.rodata
以及堆棧
均為不可執行。
所以:無法用傳統的修改GOT表來執行shellcode.
繞過的正確方法:
攻擊者可以通過程式碼重用來執行攻擊(ret2libc)。
PIE(ASLR地址隨機化)
PIE(Position-Independent Executable
) 中文解釋為地址無關可執行文件。
也叫做ASLR(地址空間隨機化
)全稱是叫(Address Space Layout Randomization
)。
這個搞過逆向的應該都不會陌生,比如搞過Windows逆向或者是iOS/macOS逆向的,我們在逆一個程式的時候。用動態調試器去調試他的時候,他的裝載地址或入口點地址,每次重啟後都是會變化的,
所以每次都需要在IDA中計算出基地址,然後再加上模組的基址才能在調試器中找到真實的記憶體地址,在iOS中則每次都要image list -o -f
來找裝載的地址。
ASLR cat /proc/sys/kernel/randomize_va_space
有三種情況:
0:表示全部關閉
1:表示部分開啟
2:表示完全開啟(在部分開啟的基礎上增加了heap的隨機化)
ASLR | Executable | PLT | Heap | Stack | Shared Libraries |
---|---|---|---|---|---|
0 | × | × | × | × | × |
1 | × | × | × | √ | √ |
2 | × | × | √ | √ | √ |
2+pie | √ | √ | √ | √ | √ |
PIE 位置無關可執行文件,在應用層的編譯器上實現,通過將程式編譯為位置無關程式碼PIC,使程式載入到任意位置,就像是一個特殊的共享庫。PIE會一定程度上影響性能。
關閉PIE/ALSR(地址隨機化)
sudo -s echo 0 > /proc/sys/kernel/randomize_va_space
gcc -fpie -pie -o test test.c // 開啟PIE,此時強度為1
gcc -fPIE -pie -o test test.c // 開啟PIE,此時為最高強度2
gcc -fpic -o test test.c // 開啟PIC,此時強度為1,不會開啟PIE
gcc -fPIC -o test test.c // 開啟PIC,此時為最高強度2,不會開啟PIE
#助記
-pie (開啟)
-no-pie(關閉)
PIE/ALSR 檢查腳本
if ${readelf} -h "${1}" 2>/dev/null | grep -q 'Type:[[:space:]]*EXEC'; then
echo_message '\033[31mNo PIE \033[m ' 'No PIE,' ' pie="no"' '"pie":"no",'
elif ${readelf} -h "${1}" 2>/dev/null | grep -q 'Type:[[:space:]]*DYN'; then
if ${readelf} -d "${1}" 2>/dev/null | grep -q 'DEBUG'; then
echo_message '\033[32mPIE enabled \033[m ' 'PIE enabled,' ' pie="yes"' '"pie":"yes",'
else
echo_message '\033[33mDSO \033[m ' 'DSO,' ' pie="dso"' '"pie":"dso",'
fi
elif ${readelf} -h "${1}" 2>/dev/null | grep -q 'Type:[[:space:]]*REL'; then
echo_message '\033[33mREL \033[m ' 'REL,' ' pie="rel"' '"pie":"rel",'
else
echo_message '\033[33mNot an ELF file\033[m ' 'Not an ELF file,' ' pie="not_elf"' '"pie":"not_elf",'
fi
FORTIFY_SOURCE
FORTIFY_SOURCE(源碼增強),這個其實有點類似與Windows中用新版Visual Studio
進行開發的時候,當你用一些危險函數比如strcpy、sprintf、strcat
,編譯器會提示你用xx_s
加強版函數。
FORTIFY_SOURCE本質上一種檢查和替換機制,對GCC和glibc的一個安全修補程式。
目前支援memcpy
, memmove
, memset
, strcpy
, strncpy
, strcat
, strncat
,sprintf
, vsprintf
, snprintf
, vsnprintf
, gets
等。
開啟關閉FORTIFY_SOURCE
#默認Ubuntu16.04下是關閉的,測試發現Ubuntu18.04是開啟的
gcc -D_FORTIFY_SOURCE=1 僅僅只在編譯時進行檢查(尤其是#include <string.h>這種文件頭)
gcc -D_FORTIFY_SOURCE=2 程式執行時也會進行檢查(如果檢查到緩衝區溢出,就會終止程式)
#助記
FORTIFY_SOURCE(程式碼增強)
-D 1(開啟緩衝區溢出攻擊檢查)
-D 2(開啟緩衝區溢出以及格式化字元串攻擊檢查) ,通過數組大小來判斷替換`strcpy、memcpy、memset`等函數名,來防止緩衝區溢出。
RELRO
RELRO全稱:Relocation Read-Only
(重定位表只讀),之前文章有介紹過動態鏈接(GOT表、PLT表)需要延遲綁定。
在啟用延時綁定時,符號的解析只發生在第一次使用的時候,該過程是通過PLT表進行的,解析完成後,相應的GOT表條目才會修改為正確的函數地址。因此,在延遲綁定的情況下,.got.plt
必須是可寫的。攻擊者就可以通過篡改地址
劫持程式。
RELRO就是為了解決延遲綁定的安全問題的,之前可以看到我們很輕鬆的用調試器修改了GOT表,達到了劫持效果,RELRO會把這些延遲綁定表設置成只讀,防止來修改。
RELRO一般有兩種形式:
Partial RELRO(部分RELRO):
Ubuntu 16.04
開始默認的GCC設置,幾乎所有的二進位文件都至少有部分RELRO。這樣僅僅只能防止全局變數上的緩衝區溢出從而覆蓋到GOT。
(.dynamic、.got
)初始化時候標記為只讀。
Full RELRO(完全RELRO):
使整個GOT只讀,從而無法被覆蓋,但是這樣會大大增加程式的啟動時間,因為需要在啟動之前解析所有的符號。
也就是延遲綁定被禁止了,所有的導入符號在開始時候被解析,.got.plt
段會被完全初始化為目標函數的最終地址
,並且被mprotect
標記為只讀
,但是.got.plt
會被合併到.got
,也就看不到這些段了,對性能會造成影響。
開啟關閉RELRO
#關閉RELRO
-z norelro 禁用relro
#開啟RELRO
-z lazy 開啟Partial RELRO(部分RELRO)
-z now FULL PARTIAL(完全開啟RELRO)
#助記
-z (RELRO)
norelro (關閉RELRO)
lazy (開啟部分RELRO)
now (完全開啟RELRO)
PWN菜雞交流小分隊
最後感謝大家的閱讀,本菜雞也是剛學,文章中如有錯誤請及時指出。
大家也可以來群里罵我哈哈哈,群里有PWN、RE、WEB大佬,歡迎交流
參考文章 :
//www.manongzj.com/blog/27-gvafmooopa.html (PWN保護機制詳解)
//www.cnblogs.com/ttxs69/p/pwn_canary.html (PWN之Canary學習)
//lzeroyuee.cn/2021/02/23/Linux-Pwn-安全機制/ (Linux Pwn – 安全機制)
//www.redhat.com/en/blog/security-technologies-stack-smashing-protection-stackguard (安全技術:堆棧粉碎保護 (StackGuard))
//clibre.io/blog/por-secciones/hardening/item/413-proteccion-de-ejecutables-fortify-source (可執行文件保護:FORTIFY_SOURCE)
《CTF權威指南(PWN篇)》