轉載自http://www.codeceo.com/article/null-pointer-01.htmlhtml
安全歷史上因爲空指針所帶來的漏洞及攻擊數不勝數,但因爲其對利用者的編程能力有要求,對分析及防禦者來講有更高的要求,因此國內對空指針漏洞及相 關技術的討論不是不少。今天這篇《空指針漏洞防禦技術》,由綠盟科技威脅響應安全專家坐堂講解,你們能夠從中瞭解空指針漏洞的基礎知識,並結合 Windows 8的內存防禦機制實例,動手實踐空指針漏洞的防禦技術。編程
指針對於絕大部分的編程人員來講都不陌生,提及C/C++中指針的使用既帶來了編程方面的方便;同時對編程人員來講,也是對我的編程能力的一種考驗,不正確的使用指針會直接致使程序崩潰,而若是是內核代碼中對指針的錯誤使用,會致使系統崩潰,後果也是至關嚴重。windows
通常狀況下咱們使用指針時,錯誤用法集中在三個方面:安全
對於第一和第二種狀況,咱們能夠經過一些代碼審計工具在發佈以前就能肯定致使內存泄露或是野指針存在的地方。好比常見的工具備fority,valgrind等及時發現指針錯誤引用致使的問題。服務器
對於第三種狀況,空指針(Null Pointer)引用致使的錯誤,依靠代碼審計工具很難發現其中的錯誤,由於空指針的引用通常不會發生在出現空指針而後直接使用空指針狀況。每每是因爲代 碼邏輯比較複雜空指針引用的位置會比較遠,不容易發現;而且在正常狀況下不會觸發,只有在特定輸入條件下才會引起空指針引用。對於排查此類錯誤也就更加困 難。網絡
本文不會重點討論內存泄露和野指針的內容,而是經過一些現有的漏洞和實例來分析一下Null Pointer 空指針。從NULL Pointer概念、本質結合靜態逆向及內核動態調試技術來了解在win7 32位和win8 32位下系統對Null Pointer處理狀況有什麼不一樣;Win8 32位針對Null Pointer添加了哪些防禦機制。函數
本文從淺入深,按部就班的講述了NULL Pointer,結合靜態逆向分析和內核級動態調試技術深刻剖析win8系統對零頁內存的保護機制,其中還涉及到了一些內核調試的技巧,對於對NULL Pointer的概念、使用比較模糊的人員值得一讀,對於從事安全研究和學習的人員也是鞏固、加深、拓展的好素材。工具
首先來看看近年來因爲空指針的錯誤使用致使的系統、服務漏洞:學習
Microsoft windows kernel ‘win32k.sys’本地權限提高漏洞(CVE-2015-1721)(MS15-061)字體
該漏洞影響了windows Server 2003 SP2和R2 SP2,Windows Vista SP2, Windows Server 2008 SP2 和R2 SP1,Windows7 SP1,Windows8 ,Windows8.1,Windows Server2012 Gold和R2,Windows RT Gold and 8.1 。利用內核驅動程序 win32k.sys漏洞能夠提高權限或是引發拒絕訪問服務,主要緣由是空指針的錯誤引用致使。
PHP空指針引用限制繞過漏洞(CVE-2015-3411)
PHP存在安全漏洞,因爲程序多個擴展中缺乏路徑或某些函數的路徑參數的空字節檢查,容許遠程攻擊者利用漏洞可繞過目標文件系統訪問限制,訪問任意文件。
0rg空指針引用拒絕訪問漏洞(CVE-2008-0153 )
X.Org是X.Org基金會運做的一個對X Window系統的官方參考實現,是開源的自由軟件。libXfont是一個用於服務器和實用程序的X字體處理庫。 X.Org libXfont 1.4.9以前版本和1.5.1以前1.5.x版本的bitmap/bdfread.c文件中的’bdfReadCharacters’函數存在安全漏 洞,該漏洞源於程序未能正確處理不能讀取的字符位圖。遠程攻擊者可藉助特製的BDF字體文件利用該漏洞形成拒絕服務(空指針逆向引用和崩潰),執行任意代 碼。
Paragma TelnetServer空指針引用拒絕服務漏洞(BID-27143)
Pragma TelnetServer是一款遠程訪問和控制Telnet服務器。Pragma TelnetServer處理協議數據時存在漏洞,遠程攻擊者可能利用此漏洞致使服務器不可用。TelnetServer服務器對每一個入站鏈接啓動一個 telnetd.exe進程,該進程在處理TELOPT PRAGMA LOGON telnet選項(138號)期間存在空指針引用,致使進程終止。儘管終止單個進程不會影響其餘進程,但終止某些進程會致使拒絕訪問服務器。
OpenSSL SSLv2客戶端空指針引用拒絕服務漏洞(CVE-2006-4343)
OpenSSL是一種開放源碼的SSL實現,用來實現網絡通訊的高強度加密,如今被普遍地用於各類網絡應用程序中。OpenSSL的協議實如今處理 鏈接請求時存在問題,遠程攻擊者可能利用此漏洞致使服務器拒絕服務。SSLv2客戶端的get_server_hello()函數沒有正確地檢查空指針。 使用OpenSSL的受影響客戶端若是建立了到惡意服務器的SSLv2鏈接,就會致使崩潰。
Linux Kernel空指針間接引用本地拒絕服務漏洞(CVE-2014-2678)
Linux kernel 3.14版本內,net/rds/iw.c中的函數rds_iw_laddr_check在實現上存在本地拒絕服務漏洞,本地用戶經過盲系統調用沒有RDS傳輸的系統上的RDS套接字,利用此漏洞可形成空指針間接引用和系統崩潰。
ISC BIND named拒絕服務漏洞(CVE-2015-5477)
ISC BIND 9.9.7-P2以前版本、9.10.2-P3以前版本,named存在安全漏洞,遠程攻擊者經過TKEY查詢,利用此漏洞可形成拒絕服務(REQUIRE斷言失敗及程序退出,指針未初始化)。
此類漏洞還有不少,從給出的幾個漏洞來看,空指針漏洞主要是以拒絕服務訪問漏洞爲主,空指針錯誤引用天然會到底程序出現錯誤,嚴重者會崩潰,從而引發拒絕服務訪問。
由空指針的錯誤引用致使的漏洞,其原理自己很簡單:錯誤引用空指針,致使非法訪問內存地址。
那到底什麼是空指針漏洞呢?在計算機編程過程當中使用指針時有兩個重要的概念:空指針和野指針。那麼在前面咱們提到的空指針漏洞是否是就是咱們在編程時所說的空指針呢?要弄清這個問題,首先須要知道編程領域中空指針和野指針分別是什麼,還要弄清楚什麼是空指針漏洞。
假如 char p,那麼p是一個指針變量,該變量尚未指向任何內存空間,若是p = 0; p = 0L; p = 」; p = 3 – 3; p = 0 * 5; 中的任何一種賦值操做以後(對於 C 來講還能夠是 p = (void)0;), p 都成爲一個空指針,由系統保證空指針不指向任何實際的對象或者函數。反過來講,任何對象或者函數的地址都不多是空指針。(好比這裏的(void)0就是一個空指針。固然除了上面的各類賦值方式以外,還能夠用 p = NULL; 來使 p 成爲一個空指針。由於在不少系統中#define NULL (void)0。
3.1.1 內存管理之空指針
空指針指向了內存的什麼地方呢?標準中並無對空指針指向內存中的什麼地方這一個問題做出規定,也就是說用哪一個具體的地址值(0×0 地址仍是某一特定地址)表示空指針取決於系統的實現。咱們常見的 空指針通常指向 0 地址,即空指針的內部用全 0來表示 (zero null pointer,零空指針)。
對於NULL能表示空指針,是否還有其餘值來表示空指針呢?在windows核心編程第五版的windows內存結構一章中,表13-1有提到 NULL指針分配的分區,其範圍是從0×00000000到0x0000FFFF。這段空間是空閒的,對於空閒的空間而言,沒有相應的物理存儲器與之相對 應,因此對這段空間來講,任何讀寫操做都是會引發異常的。
從圖中看出,對NULL指針分配的區域有0×10000之多,爲何分配如此大的空間?在定義NULL的時候,只使用了 0×00000000這麼一個值,而在表13-1有提到NULL指針分配的分區包含了0×00000000-0x0000FFFF,是否是有點浪費空間 了;這是和操做系統地址空間的分配粒度相關的,windows X86的默認分配粒度是64KB,爲了達到對齊,空間地址須要從0×00010000開始分配,故空指針的區間範圍有那麼大。
3.1.2知識拓展之野指針
「野指針」又叫」懸浮指針」不是NULL指針,是指向」垃圾」內存的指針。
「野指針」的成因主要有三種:
1)指針變量沒有被初始化。任何指針變量剛被建立時不會自動成爲NULL指針,它的缺省值是隨機的,它會亂指一氣。因此,指針變量在建立的同時應當 被初始化,要麼將指針設置爲NULL,要麼讓它指向合法的內存。例如: char *p = NULL; char *str = (char *) malloc(100);
2)指針p被free或者delete以後,沒有置爲NULL,讓人誤覺得p是個合法的指針。
free和delete只是把指針所指的內存給釋放掉,但並無把指針自己幹掉。free之後其地址仍然不變(非NULL),只是該地址對應的內存 變成垃圾內存,p成了」野指針」。若是此時不把p設置爲NULL,會讓人誤覺得p是個合法的指針。若是程序比較長,咱們有時記不住p所指的內存是否已經被 釋放,在繼續使用p以前,一般會用語句if (p != NULL)進行防錯處理。很遺憾,此時if語句起不到防錯做用,由於即使p不是NULL指針,它也不指向合法的內存塊。
用free或delete釋放了內存以後,就應當即將指針設置爲NULL,防止產生」野指針」。內存被釋放了,並不表示指針會消亡或者成了NULL指針。
3)指針操做超越了變量的做用範圍。例如不要返回指向棧內存的指針或引用,由於棧內存在函數結束時會被釋放,這種狀況讓人防不勝防,示例程序以下:
另外須要注意的是若是程序定義了一個指針,就必需要當即讓它指向一個咱們設定的空間或者把它設爲NULL,若是沒有這麼作,那麼這個指針裏的內容是 不可預知的,即不知道它指向內存中的哪一個空間(即野指針),它有可能指向的是一個空白的內存區域,可能指向的是已經受保護的區域,甚至可能指向系統的關鍵 內存,若是是那樣就糟了,也許咱們後面不當心對指針進行操做就有可能讓系統出現紊亂,死機了。因此必須設定一個空間讓指針指向它,或者把指針設爲 NULL。
接下來就結合兩個小的實驗例子看看空指針,及使用空指針的後果。
3.2.1指針使用前期對比
本例是要驗證指針變量在初始化先後的情況,下圖是一段代碼:
代碼很簡單只是驗證指針變量在賦值與未賦值分別指向的空間。代碼中要打印的值:
編譯以後,運行三次程序看運行的結果:
從三次運行結果來看:
3.2.2指針使用後期對比
針對指針變量在使用完畢後,內存釋放以後的狀況對比,其源碼以下:
代碼沒什麼複雜邏輯,只是想驗證一下在指針變量釋放後, P=NULL以前的野指針與P=NULL以後的指針情況,打印的內容:
下圖是在編譯以後運行的結果:
a. 指針變量在聲明以後一直到最後的程序結束,指針變量的地址一直沒有變更爲0x35f778。 b. 定義指針變量時其值爲0×00000000 c. 在申請新的內存以後,指針變量的值爲申請內存的地址0x6e7a78 d. 在內存釋放後,P變成野指針,可是此時P的值仍然是申請的內存地址0x6e7a78,可是此時P指針已經不能再使用。 e. 在P=NULL,p從新指向了地址0×00000000處。
因而可知,咱們在釋放指針變量後,指針變量會變成野指針,若是此時引用該指針會出現非法訪問,所以須要在釋放指針變量後將指針變量指向空。
爲了可以更加直觀的查看指針變量在內存中的變化,下面就結合動態調試的技術看看指針變量在整個使用過程當中的變化狀況。下面是一段程序代碼:
在這裏,咱們使用OD動態調試器,來看看調用printf函數的狀況:
a. 第一條打印語句
printf("p address :%p, value :%08x\n", &p, p);
在調用printf前其內存狀況:
可知指針變量P的值爲EAX=[EBP-4],P的地址ECX=EBP-4,此時的寄存器值:
調用printf以後的值恰好是這兩個值
b.第二條打印語句printf(「p intialized address :%p, value :%08x\n」, &p, p);在調用printf前其內存狀況:
可知指針變量P的值爲EDX=[EBP-4],P的地址EAX=EBP-4,此時的寄存器值:
調用printf以後的值恰好是這兩個值:
c. 第三條打印語句printf(「p free address :%p, value :%08x\n」, &p, p);在調用printf前其內存狀況:
可知指針變量P的值爲EAX=[EBP-4],P的地址ECX=EBP-4,此時的寄存器值:
調用printf以後的值恰好是這兩個值:
d. 第四條打印語句printf(「p ultimate address :%p, value :%08x\n」, &p, p);在調用printf前其內存狀況:
可知指針變量P的值爲EDX=[EBP-4],P的地址EAX=EBP-4,此時的寄存器值:
調用printf以後的值恰好是這兩個值:
前面對什麼是空指針,什麼是野指針作了講解和驗證。
在編程領域的空指針是指向NULL的指針,也就是說指向零頁內存的指針叫空指針。對於未初始化的指針,釋放內存而未將指針置爲NULL和指針指向超 出範圍的狀況稱爲野指針。那麼在第二節中列舉的空指針漏洞及未列舉的空指針漏洞是否是都是因爲引用零頁內存致使的呢(好比CVE-2014-2678)? 其實否則,有些漏洞是因爲引用未初始化的指針或是引用超出範圍的指針所致使,而這類漏洞應該說是因爲錯誤的引用了野指針。好比最新的BIND漏洞 (CVE-2015-5477):
可是到目前爲止還沒用據說過哪一個漏洞命名爲野指針漏洞,而更多的是空指針漏洞。也就是說在計算機安全領域中由空指針或是野指針致使的漏洞統一叫作空指針漏洞。