基於棧式的緩衝區溢位
緩衝區溢位攻擊是一個老生常談的問題了,實際上我們所在的這個世界上,每天無時無刻不在發生各種各樣的緩衝區溢位的攻擊,隨著反制技術的不斷升級,這些攻擊技巧都在大量進行升級還貸,但,所謂哪裡有壓迫哪裡就有反抗!
ASLR(地址隨機化)、DEP(資料執行保護)、ASCII armoring(地址插零),GS(呼叫 cookie)等方面的反制技術讓攻擊變的越來越困難,當人類被壓迫的時候會想出各式各樣的方法來解決這些問題,只是變得困難了但不是說不能進行攻擊,例:DEP 保護 “rop_chains、ret2libc” 可以繞過它們,GS 呼叫 cookie 保護,有兩類方式繞過,一靠猜【蒙】函式的呼叫 cookie 值(這是一個固定的值)另一種是想辦法利用溢位強行覆蓋 “SEH(結構化異常處理)catch_chains” 中的虛擬函式處理地址
ASCII armoring 保護;就是想辦法讓每個被載入程式地址空間的 libc 函式的地址中都具有一個 NULL 位元組,從而讓通過字串拷貝的 libc 的地址無效,讓攻擊無從談起,但人們的智慧是無窮的,當遇到了壓迫人們就會奮起的進行反抗,所以 ret2plt 技術應用而生,它利用 PPR(pop %ebx pop %ebp retn)指令序組成一段連續的 strcpy or memcpy 函式把要被呼叫的函式地址的位元組從不同的空間拷貝到棧上,然後在進行呼叫,從而繞過 “ASCII armoring” 的保護機制。
上面提到這些攻擊技術幾乎都是利用棧溢位攻擊的,那麼堆溢位是不是就真的安全?顯然不是的,堆溢位攻擊(unlink)相信很多人可能沒有聽過這個東西,這是一種專門 “堆溢位攻擊” 提出的一門技術,它就是想辦法利用目標程式的 “堆溢位” 的漏洞,把分配的 “堆記憶體” 的 “chunk header” 中的虛擬函式地址給覆蓋了,malloc 分配的記憶體是連續的,即 malloc 兩次記憶體之間的地址是相互連線在一起的(從高到低,跟棧差不多)而 “堆溢位” 就是想辦法覆蓋掉 “下個 malloc” 函式分配的 “chunk header” 中的 free addr 當 “程式” 呼叫第二次 free 函式時就會執行攻擊的 shellcode。
所以沒有真正安全程式與技術,它只是相對的,但這對我們來說有多大的意義?我本人在這裡不說推銷這些技術,而是首先我們需要明白一點,技術本身是無罪的,犯罪的只是別有用心的人而已,大多數所謂進行不法活動的人都是些在某些 XX 群裡面交了點學費,拿了點資料跟工具就出來禍害人的 “小害蟲”,提供工具的人水平不用質疑,但是沒用到正道上面!
我們瞭解與研究這些技術,並不一定需要真的就去 “專精” 它們,因為我們想要沐浴在 “陽光之下”,不過這個也需要看每個人的選擇,或許它們覺得這很 geek ,當然走 IA / MA / SA 也很 geek,只是大多數人不知道而已,它們之間只是發展方向上的不同而已,大多數做安全更多還是去搞 “競品分析”,地位也不能和 2010 年之前相比了。
我個體只想走 “陽光正道”,不喜歡走這些東西,而且我的確從打心底來說,並不是那種特別喜歡這些東西的人,但我還是要了解與研究它們?原因是為了更好的防護的我的 “程式” 當我不知道它們的攻擊手法,你不可能在軟體在做 “瀑布設計” 與 “敏捷開發” 過程中兼顧並考慮到這些問題,那麼又從何去解決問題減少損失?坐等 “防毒流氓、微軟” 這些去打補丁?或者說不用考慮的直接關閉我的程式或伺服器?但當我們知曉了我自然有更好的辦法能夠解決這些問題,減少損失不是更好?
本文只提供 jmp esp 的方式,雖然這個方式在現代來說根本不能用,但是就瞭解緩衝區溢位攻擊是一個很不錯的入門試驗,後面我可能會寫基於 ROP_chains 或 ret2libc 攻擊的利用方式,但它們本質是想通的,只是變得更加的麻煩跟費神,但不是說不能應用,其實市面上還有很多可以通過這幾類方法利用的漏洞,你不得不說大多數人包括公司的安全意識真的太差勁了。
但是要執行本文給出的程式碼,你必須要在 C/C++ 編譯器與聯結器配置中關閉掉 “GS” 與 “DEP”,DEP主要是為了防止 “不具有 PAGE_EXECUTE_READ ” 記憶體標誌,許可權的二進位制程式碼資料的執行,而 JMP ESP 是把 shellcode 複製到棧上執行的,而棧上的記憶體是不具有 “PAGE_EXECUTE_READ” 的許可權的,只有 PAGE_READWRITE 的許可權,不過這個問題可以利用 ROP_chains 進行繞過。
參考上述要進行試驗的 C/C++ 配置,說實話這個東西,如果靠給人講其實並不是形象(有點抽象)或許說了很多其實到最後面人們可能還是沒有理解,所以本文將提供可被編譯且正確執行的 “C語言示意程式碼” 用於闡述本文的內容。
#include <stdlib.h>
#include <stdio.h>static unsigned char shellcode[] =
{
0,0,0,0, // ebp-8
0,0,0,0, // ebp-4
0,0,0,0, // ebp-0
0,0,0,0, // ebp+4(retn addr == RIP) -- JMP ESP
//
184,0,0,0,0, // MOV EAX,fun_hijacked
255,224, // JMP EAX
};
static void fun_hijacked()
{
printf("Attack successful");
getchar();
exit(0);
}
static void fun_bug()
{
int n; // ebp-8
memcpy(&n, shellcode, sizeof(shellcode));
}
static void fun_def_jmp_esp()
{
__asm
{
jmp esp
jmp esp
jmp esp
}
}
static void* fun_get_jmp_esp_addr()
{
unsigned char* p = &fun_def_jmp_esp;
while (1)
{
if (p[0] == 0xFF && p[1] == 0xE4) // jmp esp
{
return p;
}
p++;
}
}
int main(int argc, char* argv[])
{
*(void**)&shellcode[12] = fun_get_jmp_esp_addr();
*(void**)&shellcode[17] = &fun_hijacked;fun_bug();
}