2015/5/12 18:36:42
時隔這麼久,自己終於重新開始了安全之路,雖然每天的工作和研讀的論文也都是安全領域的技術,但是自己心裏真正的安全還是漏洞的研究。因此,自己決定業餘來自學這部分,今天算是一個正式的開始吧!
今天來簡單介紹下Linux漏洞,因爲Linux給予了用戶更大的自由度和操控性,因此更加適合進行安全研究,我們也從Linux開始。這部分主要分爲以下小節:
棧的操作
緩衝區溢出的基本原理
本地緩衝區溢出漏洞***框架
小緩衝區的漏洞***框架
一、棧的操作
棧是操作系統進程內存中特有的結構,當系統運行一個程序,程序執行所需要的信息、數據都會以進程的形式存放到內存中供隨時讀寫,這時內存中程序代碼的大致結構就像下面這個樣子:
棧是一種特殊的數據結構,可以想象出餐館疊在一起的盤子,每次放上一個新盤子,之前最上面的盤子就到了下面,新盤子成爲了最頂上的盤子;而取盤子時,總是從上向下依次取出,這樣的數據結構我們稱之爲先進後出(FILO),棧就是一種這樣的結構。
棧的增長順序,在任何一個系統中都是由高內存向低內存擴展,我能想到一個直觀的理由是系統默認可以從低內存開始處理數據(不考慮大端存儲的話)。
棧有兩個基本的操作,一個是將數據/元素壓入棧中,即PUSH操作;另一個則相反,是將棧頂的元素彈出,存儲到寄存器中,即POP操作。
內存中每個進程在內存棧段中都有自己的棧,而且棧始終是由最高內存地址向最低內存地址反向增長的。關於棧有兩個重要的寄存器,一個是擴展基地址指針EBP,它指向棧底,即較高地址;另一個是擴展棧頂指針ESP,指向棧的較低地址。
系統在實際運行程序時,棧的應用往往表現在函數調用。每個函數都會有自己的棧,因此當發生一次函數調用時,調用函數和被調用函數都要進行一些棧的“交接”。比如對於調用函數來說,需要做以下工作:
調用程序首先將被調用函數的參數按照逆序壓入棧中,從而對函數調用進行設置;
將當前的EIP指針(擴展指令指針,只向CPU下一步要處理的指令)保存到棧上,這樣在被調用函數返回時就可以在離開的地方繼續執行,這個地址被稱作返回地址;
執行call命令,將被調用函數的地址放入EIP中執行;
這樣,調用函數將當前的運行狀態完整保存到棧中,同時壓入了被調用函數的參數,接下來,就是被調用函數的工作了,我們一般稱之爲“開場白”:
“開場白”:保存當前EBP到棧中,然後將ESP設置爲EBP,然後ESP減小,留出存放變量的空間;
“收場白”:其實要簡單些,這裏主要是將ESP增加到EBP,釋放清空棧,然後彈出EIP,繼續之前中斷的執行,一般就是利用leave和ret兩個指令完成。
我在自己的虛擬機Ubuntu12.04上編寫了一個簡單的C程序,然後使用disassemble命令反彙編程序文件,可以看到清晰的“開場白”和“收場白”:
二、緩衝區溢出
緩衝區,本身是系統用於臨時存放數據的一塊內存區域,我們經常會用到變量複製的操作,將一個緩衝區的數據複製到另一個緩衝區,但是遺憾地是並非所有的函數都執行了嚴格的緩衝區大小檢查操作。因此,當向一個緩衝區中複製超過其大小的數據時,數據就會超出其邊界,覆蓋影響到其相鄰的區域。
假設我們現在有一個具有兩個參數的函數調用過程,我們通過此時緩衝區在內存中的結構來說明下緩衝區溢出的原理。
在被調用函數得到執行權之前,調用函數會講被調用函數的參數逆序壓進棧中,這裏就是Temp1和Temp2,然後壓棧保存當前的EIP指針,最後利用Call命令將被調用函數的地址設置爲EIP開始執行。
被調用函數的棧從EBP開始,到ESP爲止,中間可以是函數內部定義的局部變量。如果函數內部定義了一個變量name是一個10字節的數組,當將一個100字節的Temp2複製到name時無疑會出現數據的越界,比如覆蓋到EBP,甚至到達EIP,而此時原先的EIP便遭到了破壞,造成了緩衝區溢出。
緩衝區溢出的結果一般會造成拒絕服務,但是這是一個相對比較好的結果,因爲起碼程序給出了提示,有些時候EIP會被***者控制並以用戶級訪問權限執行惡意代碼。而第三種則是最糟糕的情況,也就是EIP被控制並在系統級或根級執行惡意代碼,此時***者獲得了系統最高權限。
三、本地緩衝區溢出漏洞***
本地緩衝區溢出漏洞***要比遠程漏洞***容易些,因爲能夠訪問系統內存空間,並且更容易調試***代碼。
一般一個漏洞***程序由三個部分構成:
NOP雪橇:彙編代碼中有一個空指令NOP,意味着不執行任何操作,而只是移動到嚇一跳命令。緩衝區溢出***的思路是通過緩衝區溢出覆蓋控制EIP,使得EIP跳轉到我們的***代碼,而有時控制計算的並非十分精確,現實中也存在多種情況,因此我們需要添加一段NOP指令作爲返回地址的“緩衝”,以防EIP超出***代碼的範圍。
Shellcode:上面所說EIP需要指向一個***代碼,這個代碼就是我們的Shellcode了。原本Shellcode的作用僅僅是返回一個執行shell,也是因此得名,但是現在的Shellcode可以執行更爲複雜的***。Shellcode是執行***命令的機器代碼,因此實質上是二進制碼,通常以十六進制表示。
重複返回地址:確定好NOP雪橇和Shellcode的地址後,就需要確定EIP指向的地址,並且進行一定程度的重複,作爲緩衝區溢出的填充,一定要覆蓋到EIP,作爲EIP指向Shellcode纔可以實施***。
最後,我們將以上三個部分組合起來,就是一個緩衝區溢出***的框架:
四、小緩衝區的溢出漏洞***
一般Shellcode不會太大,但是也不會太小,如果我們發現存在漏洞的緩衝區很小,不足以存放上面的Shellcode,那該如何呢?
方法就是將Shellcode存放到環境變量的位置,然後緩衝區覆蓋EIP指向Shellcode。
這樣之所以可以成功,是因爲所有Linux ELF文件在映射到內存中時會將最後的相對地址設爲0xbfffffff。環境變量和參數存儲在這個區域,這些數據的下面就是函數棧,結構如圖:
這樣,我們就可以繼續將EIP指向Shellcode執行***。
PS:
好了,Linux漏洞的基礎知識就介紹到這裏了,一些內存、堆棧的知識可以參考我之前的文章。今天雖然講漏洞,但是沒有列出代碼和實驗演示,感興趣地朋友可以去Metasploit的網站瀏覽更多信息,那裏有許多***實驗的分享。
Refer: Gray Hat Hacking: The Ethical Hacker's Handbook, Third Edition