最近我在Linux上利用CERT BFF研究一個PoC。我之前有大量在Windows上寫PoC的經(jīng)驗,所以認為將這種技能擴展到不同平臺上是個不錯的想法。可是,當我獲得指令指針的控制權(quán)后,卻發(fā)現(xiàn)幾乎可以毫無障礙地使代碼執(zhí)行。
除了對Metasploit字符串做一個BFF最小化之外,不需要額外的工作,就可以很容易知道哪些字節(jié)在我的控制之下,以及怎么執(zhí)行到那里:
運行起來之后,就控制了EIP(0×61626364 = "abcd"),得到一個Metasploit字符串模式,以及指向那里(EAX)的寄存器。下面我們看看這個位置的內(nèi)存保護情況
不會是真的吧,這部分堆內(nèi)存居然有讀、寫和執(zhí)行權(quán)限!任意一塊內(nèi)存既有可寫權(quán)限又有可執(zhí)行權(quán)限都是一件非常危險的事情。為什么呢?以上述這種情況為例,其允許攻擊者將代碼跳轉(zhuǎn)到這里,并執(zhí)行這些被系統(tǒng)認為是數(shù)據(jù)的字節(jié)。如果一塊內(nèi)存區(qū)域被標記為可執(zhí)行,NX是不會起作用的。(參考OpenBSD的W^X策略。)
我們需要花點時間找出為什么這塊堆內(nèi)存是可執(zhí)行的。我使用的平臺是UbuFuzz,基于Ubuntu 12.04.01,內(nèi)核是x86 3.2.0。該平臺上,如果指定一塊可執(zhí)行棧,例如用gcc編譯時指定-z execstack選項,進程的READ_IMPLIES_EXEC屬性就會被設置。正如其名,如果該位被設置,那么可讀的內(nèi)存區(qū)域默認也同時變成可寫的了,包括堆內(nèi)存(heap)。
問題是在崩潰進程的ELF頭部并沒有指定可執(zhí)行棧:
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4如果進程在編譯時沒有指定,默認是不會包含可執(zhí)行棧的,但要是指定了READ_IMPLIES_EXEC特性會怎么樣呢?而且它也沒有顯式調(diào)用personality()函數(shù)。這就是比較奇怪的地方。如果崩潰的進程不是從終端直接調(diào)用的,而是由另一個不同的應用程序生成的,會怎么樣呢?下面我們看看其繼承自父進程的棧屬性:
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4罪魁禍首找到了!其同時也證明了在x86 Linux平臺,READ_IMPLIES_EXEC屬性位會從父進程傳遞給子進程。這種行為是架構(gòu)決定的,因為在amd64架構(gòu)的平臺上,子進程就不會繼承READ_IMPLIES_EXEC特性。但在所有架構(gòu)的平臺上,用來禁止ASLR的ADDR_NO_RANDOMIZE標記都會被子進程繼承。
總結(jié)
在vanilla Linux平臺,對于任意給定的進程,很難靜態(tài)地確定其啟用了哪些漏洞防護措施。在目前流行的X86平臺的Linux上,任何正在運行的進程都可能沒有啟用NX保護,只是因為其父進程沒有啟用相應的保護。
下一篇:最新防御黑客的攻擊教程