編譯型應用程序及程序架構攻防

編譯型應用程序與應用程序架構攻防html

孟飛陽  20120228程序員

1三、攻擊編譯型應用程序

13.1  緩衝區溢出漏洞

若是應用程序將用戶可控制的數據複製到一個不足以容納他們的內存緩存區,就會出現緩衝區溢出漏洞。因爲目標緩衝區溢出,致使鄰近的內存被用戶數據覆寫。攻擊者能夠根據漏洞的本質利用它在服務器上運行任意代碼或執行其餘未受權操做。多年來,緩衝區溢出漏洞一直在本地軟件中廣泛存在,並被視爲本地軟件開發者必須避免的「頭號公敵」。算法

13.1.1  棧溢出

若是應用程序在未肯定大小固定的緩衝區容量足夠大以前,就使用一個無限制的複製操做(如C語言中的Strcpy)將一個大小可變的緩衝區複製到另外一個大小固定的緩衝區中,每每就會形成緩衝區溢出。例如,下面的函數將字符串username複製到一個分配到棧上的大小固定的緩衝區中:shell

bool CheckLogin(Char* username,char* password){

char _username[32];
strcpy(_username,username);
...

}

若是字符串username超過32字符,_username緩衝區就會溢出,攻擊者將覆寫鄰近內存中的數據。在成功利用棧緩衝區溢出漏洞的攻擊中,攻擊者一般可以覆寫棧上已保存的返回地址。當調用CheckLogin函數時,處理器將調用函數後執行的指令地址寫入棧。結束CheckLogin函數後,處理器從棧中取出這個地址,返回執行這個指令。同時,CheckLogin函數分配到棧上已保存的返回地址旁邊的_username緩衝區。若是攻擊者可以令_username緩衝區溢出,他就能用他選擇的一個值覆寫緩衝區已保存的返回地址,讓處理器訪問這個地址,從而執行任意代碼。數據庫

13.1.2  堆溢出

從本質上講,緩衝區溢出也是由前面描述的相同危險操做形成的,惟一不一樣在於這時溢出的目標緩衝區分配在堆上,而不是在棧上:編程

bool CheckLogin(char* username,char* password){
char* _username = (char*) malloc(32);

strcpy(_username,username);

...

一般,在堆緩衝區溢出中,目標緩衝區旁不是已保存的返回地址,而是其餘以堆控制結構分隔的堆內存塊。堆以一個雙向鏈接表的形式執行:在內存中,每一個塊的前面是一個控制結構,其中包含塊的大小、一個指向堆上前一個塊的指針以及一個指向堆上後一個塊的指針。當堆緩衝區溢出時,鄰近的堆塊的控制結構被用戶控制的數據覆寫。瀏覽器

與棧溢出漏洞相比,利用這種漏洞實施攻擊更要困難一些;可是,一種常見的利用方法是在被覆寫的堆控制結構中寫入專門設計的值,以在未來某個時間覆寫任何一個關鍵的指針。控制結構已被覆寫的堆塊從內存中釋放後,堆管理器須要更新堆塊的鏈接表。要完成這項任務,它須要更新後一個堆塊的反向連接指針,並更新前一個堆塊的正向連接指針,以便連接表中的這兩個指針指向彼此。爲此,堆管理器使用被覆寫的控制結構中的值。具體來講,爲更新後的一個塊的反向連接指針,堆管理器廢棄被覆寫的控制結構中的正向連接指針,並在這個地址的結構中寫入被覆寫的控制結構中的反向連接指針的值。換句話說,就是一個用戶控制的地址中寫入一個用戶控制的值。若是攻擊者精心設計了他得溢出數據,他就能用它選擇的值覆寫內存中的任何指針,其目的是控制指針的執行路徑,從而執行任意代碼。一般,指針覆寫的主要目標是隨後被應用程序調用的函數指針的值,或者是在下次出現異常時被調用的異常處理器的地址。緩存

最新的編譯器與操做系統已經採起了各類措施對軟件進行保護,防止編程錯誤致使緩衝區溢出。這表示,現在現實世界中的溢出漏洞每每比這裏描述的示例更難以利用。安全

13.1.3  「一位偏移」漏洞

若是編程錯誤使得攻擊者能夠在一個被分配的緩衝區以後寫入一個字節(或少數幾個字節),就會發生一種特殊的溢出漏洞。服務器

一些語言(如C)並不單獨記錄一個字符串的長度,字符串結束部分用一個空字節表示(也就是說,用零的ASCII字節編碼表示)。若是一個字符串「丟失」了它的空終止符,它的長度就會增長,並一直到內存的下一個字節(它碰巧爲零)結束。這種無心的結果常常會再應用程序中形成反常行爲與漏洞。

咱們曾在一個硬件設備的Web應用程序中發現這種漏洞。該應用程序包含一個頁面,它接受POST請求的任意參數,並返回HTML表單,其中以隱藏字段的形式包含那些參數的名稱與參數值。例如:

POST /formRelay.cgi HTTP/1.0
Content-Length:3
a = b
HTTP/1.1  200 OK
Date:THU,02 NOV 2006 14:53:13 GMT
Content-Type :text/html
Content-Length:278

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=iso-8859-1">
</head>
<form name="FORM_RELAY" action="page_cgi" method="POST">
<input type="hidden" name="a" value="b">
</form>
<body onLoad="document.FORM_RELAY.submit();">
</body>
</html>

由於某種緣由,真個應用程序都須要使用這個頁面處理各類用戶輸入,其中許多爲敏感數據。然而,若是用戶提交的數據等於或超過4096B,那麼返回的表單中還包括在向頁面提出的前一個請求中提交的參數,即便這些參數由另一名用戶提交。

肯定這種漏洞後,咱們就能夠繼續向這個易受攻擊的頁面提交超長數據,解析收到的響應,記錄其餘用戶提交給頁面的每個數據,包括登錄證書和其餘敏感信息。

形成這種漏洞的根本緣由是,在4096B的內存塊中,用戶提交的數據被保存爲以空字節終止的字符串。這些數據被複制到一個檢驗操做中,所以不會直接形成溢出。然而,若是提交的是超長得輸入,複製操做就會致使空終止符「丟失」,於是字符串會「流入」到內存鄰近的數據中。所以,當應用程序解析請求參數時,它會一直解析到下一個空字節爲止,所以就會解析出其餘用戶提交的參數。

13.1.4  查找緩衝區溢出漏洞

向一個肯定目標發送超長的字符串並監控反常結果是查找緩衝區溢出漏洞的基本方法。有些時候,一些細微的漏洞只有經過發送一個特殊長度或者在較小的長度範圍內的超長字符串才能檢測出來。可是,許多時候,只需嚮應用程序發送一個超出其預計長度的字符串,就能夠探查出漏洞。

程序員經常使用十進制或十六進制的約整數(如3二、100、102四、4096等)來建立固定大小的緩衝區。在應用程序中探查明顯漏洞的一個簡單方法就是,向肯定的每個目標數據發送超長字符串,而後監控服務器對反常輸入的響應。

滲透測試步驟:

(1)向每個目標數據提交一系列稍大於經常使用緩衝區大小的字符串。如:1100、4200、33000

(2)一次針對一個數據實施攻擊,最大程度的覆蓋應用程序中的全部代碼路徑。

(3)可使用Burp Intruder中的字符塊有效載荷來源自動生成各類大小的有效載荷。

(4)監控應用程序的響應,肯定全部反常現象。沒法控制的溢出幾乎能夠確定會在應用程序中引發異常。在遠程進程中探測什麼時候出現這種異常至關困難,須要尋找的反常現象包括如下幾項。

HTTP500狀態碼或錯誤消息,這時其餘畸形(而非超長)輸入不會產生相同的結果。

內從詳細的消息,表示某個本地代碼組件發生故障。

服務器收到一個局部或畸形響應

服務器的TCP連接未返回響應,忽然關閉。

整個Web應用程序中止響應。

(5)注意,若是一個堆溢出被觸發,這可能會在未來而非當即致使系統崩潰。所以,必須進行實驗,肯定一種或幾種形成堆「腐化」的測試字符串。

(6)「一位偏移」漏洞可能不會造車系統崩潰,但可能會致使反常行爲,如應用程序返回出人意外的數據。

有些時候,測試字符串可能會被應用程序自身或其餘組件(如Web服務器)實施的輸入確認檢查所阻止。在URL查詢字符串中提交超長數據時一般會出現這種狀況,應用程序會再針對每一個測試字符串的響應中以「URL過長」之類的常規消息反映這一點。在這種狀況下,應當進行實驗,肯定URL容許的最大長度(通常約爲2000個字符),並調整緩衝區大小,以使測試字符串符合這個要求。可是,即便實施了常規過濾,溢出可能依然存在;由於長度足夠短、可以避開這種過濾的字符串也可能觸發溢出。

其餘狀況下,過濾機制可能會限制一個特定參數中提交的數據類型或字符範圍。例如,當將提交的用戶名傳送給一個包含溢出漏洞的功能時,應用程序會確認該用戶名是否包含字母數字字符。爲實現測試效率最大化,滲透測試員應當設法確保每一個測試字符串僅包含相關參數容許的字符。知足這種要求的一個有效方法是,截獲一個包含應用程序所接受的數據的正常請求,而後使用其中已經包含的相同類型的字符,建立一個可能經過任何基於內容的過濾的長字符串,在使用這個字符串輪流測試每個目標參數。即便確信應用程序中存在緩衝區溢出漏洞,可是,要遠程利用它執行任意代碼仍然及其困難。

13.2  整數漏洞

若是應用程序在執行某種緩衝區操做前對一個長度值運用某種算法,但卻沒有考慮到編譯器與處理器整數計算方面的一些特色,每每就會出現與整數有關的漏洞。有兩種類型的漏洞最值得關注:整數溢出與符號錯誤。

13.2.1  整數溢出

當對一個整數值進行操做時,若是整數大於它的最大可能值或小於它的最小可能值,就會形成整數溢出漏洞。這時,數字就會「迴繞」,使得一個很是大的數字變得很是小,或者與之相反。下面之前面堆溢出漏洞的「修復」代碼爲例:

bool CheckLogin(char* username,char* password){

unsigned short len=strlen(username) + 1;

char* _username = (char*)malloc(len);

strcpy(_username,username);

..

在這段代碼中,應用程序求出用戶提交的用戶名的長度,增長一個長度安置字符串最後的空字節,在給他分配一個相應長度的緩衝區,而後將用戶名複製到這個緩衝區內。若是使用正常長度的輸入,這段代碼就可以正常運行。可是,若是用戶提交一個65535個字符的用戶名,就會形成整數溢出。一個長度較短的整數包含16位,它足以保存0~65535。若是提交一個長度位65535的字符串,程序會在這個字符串後面增長一個長度,使得這個值「迴繞」而變成0。因而應用程序爲他分配一個長度爲0的緩衝區,把用戶名複製到它裏面於是形成堆語出。這樣,即便程序員試圖確保目標緩衝區足夠大,攻擊者仍然可以製造溢出。

13.2.2  符號溢出

若是應用程序使用有符號和無符號的整數來表示緩衝區的長度,而且在某個地方混淆這兩個整數,或者將一個有符號的的值與無符號的值進行直接比較,或者向一個僅接受無符號的值得函數參數提交有符號的值,都會出現符號錯誤。在上述兩種狀況下,有符號的值都會被當作其對應的無符號的值處理,也就是說,一個負數變成一個大整數。下面之前面棧溢出漏洞的修復「代碼」爲例:

bool CheckLogin(char* username,int len,char* password){

char _username[32]="";

if(len < 32)

  strcpy(_username,username,len);

...

這這段代碼中,函數以用戶提交的用戶名和一個表示其長度的有符號整數爲參數。程序員在棧上創建一個固定大小的緩衝區,檢查用戶名的長度是否小於緩衝區的大小,若是是這樣,就執行計數緩衝區複製,確保緩衝區不會溢出。

若是len參數爲負數,這段代碼就可以正常運行。然而,若是攻擊者可以向函數提交一個負值,那麼程序員的保護性檢查就會失效。仍然能夠成功將它與32進行比較,由於編譯器會把這兩個數字當作有符號的整數處理。所以,這個負值被提交給strncpy函數,成爲它的計數函數。由於scrncpy僅接受無符號的整數爲參數,因此編譯器將len值隱含的轉換成這種類型;於是負值被當作一個大得整數處理。若是用戶提交的用戶名字符串長度大於32B,那麼緩衝區就會溢出;這種狀況和標準棧溢出相似。

一般,實施這種攻擊必須知足一個前提,即長度參數由攻擊者直接控制。例如,它由客戶端JavaScript計算,並在請求中將它所屬的字符串一塊兒提交。可是,若是整數變量足夠小(例如,很是短)且程序在服務器端計算它的長度,那麼攻擊者仍然能夠經過嚮應用程序提交一個超長的字符串,藉由整數溢出引入一個負值。

13.2.3  查找整數漏洞

天然的,任什麼時候候,只要客戶向服務器提交整數值,咱們就能夠在這些位置探查整數漏洞。一般這種行爲發生在如下兩種不一樣的狀況下。

(1)應用程序經過查詢字符串參數、cookie或消息主體,以正常形式提交整數值。這些數字通常使用標準的ASCII字符,以十進制表示。這時,表示一個一樣被提交的字符串長度的字段使咱們測試的主要目標。

(2)另外,應用程序可能提交嵌入到二進制數據對象中的整數值。這些數據可能源自一個客戶端組件,如ActiveX控件;或者經過客戶在隱藏表單字段或cookie中傳送。在這種狀況下,與長度有關的整數漏洞更難以發現。他們通常以十六進制的形式表示,一般出如今與其關聯的字符串或緩衝區以前。請注意,上述二進制數據可能會經過Base64或相似的方案編碼,以便於經過HTTP傳送。

滲透測試步驟:

(1)肯定測試目標後,須要提交適當的有效載荷,以觸發任何漏洞。輪流向每個目標數據發送一系列不一樣的值,分別表示不一樣有符號與無符號整數值的邊界狀況。例如:

    Ox7f與Ox80(127與128)

    Oxff與Ox100(255與256)

    Ox7ffff與Ox8000(32767與32768)

    Oxffff與Ox10000(65535與65536)

    Ox7fffffff與Ox80000000(2147483647與2147483648)

    Oxffffffff與Ox0(4294967295與0)

(2)若是被修改的數據以十六進制表示,應該發送每一個測試字符串的little-endian與big-endian版本,例如,ff7f與7fff。若是十六進制數字以ASCII形式提交,應該使用應用程序自身使用的字母字符,確保這些字符被正確編碼

(3)與上述查找緩衝區溢出漏洞時同樣,應該監控應用程序響應中出現的反常事件。

13.3  格式化字符串漏洞

若是用戶可控制的輸入被當作格式化字符串參數提交給一個可接受可能被濫用的格式說明符的函數(如C語言中的printf系列函數),就會產生格式化字符串漏洞。這些函數接受的參數數量不定,其中可能包含不一樣的數據類型,如數字和字符串。提交給函數的格式化字符串中包含的說明符告訴函數:變量參數中應包含何種數據,以及這些數據以什麼格式表示。

例如,下面的代碼輸出一條包含以十進制表示的count變量值得消息:

printf("The value of count is %d",count.);

最危險的格式說明符爲%n。這個說明符不會致使什麼數據被打印。相反,它使得已經輸出的字節數量被寫入到以相關變量參數提交給函數的指針地址中。例如:

int count=43;
int written=0;

printf("The value of count is %d%n.\n",count,&written.);
printf("%d bytes were printed.\n",written);

它輸出:

The value of count is 43.
24 bytes were printed.

若是格式化字符串中的說明符比提交給函數的變量參數多,而函數又沒法探查到這一點,那麼它就會繼續處理調用棧中的參數。

若是攻擊者可以控制提交給printf之類函數的所有或部分格式化字符串,他就能夠利用上述行爲覆寫進程內存的重要部分,並最終執行任意代碼。因爲攻擊者控制着格式化字符串,因此它可以控制函數輸出的字節數量以及棧上被輸入的字節數量覆寫的指針。這樣,攻擊者就可以覆寫一個已保存的返回地址或者一個指向異常處理器的指針,進而控制代碼執行,就向在棧溢出中同樣。

13.3.1  查找格式化字符串漏洞

在遠程應用程序中探查格式化字符串漏洞的最有效方法是,提交包含各類格式說明符的數據,並監控應用程序的任何反常行爲。與不受控制地觸發緩衝區溢出漏洞可能形成的後果同樣,在一個易受攻擊的應用程序探查格式化字符串漏洞可能會致使系統崩潰。

滲透測試步驟:

(1)輪流向每一個目標參數提交包含大量格式化說明符%n與%s的字符串:

%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n

%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s

注意,基於安全考慮,一些格式化字符串操做可能會忽略%n說明符。相反,提交%s說明符將會使函數廢棄棧上的每個參數,若是應用程序易於受到攻擊,就可能會致使非法訪問。

    Windows  FormatMessage函數以一種不一樣的方式使用printf系列函數中的說明符。爲測試調用這個函數是否易於受到攻擊,應該使用如下字符串:

%1!n!%2!n!%3!n!%4!n!%5!n!%6!n!%7!n!%8!n!%9!n!%10!n!  etc...

%1!s!%2!s!%3!s!%4!s!%5!s!%6!s!%7!s!%8!s!%9!s!%10!s!  etc...

記得將%字符URL編碼成%25

與上述查找緩衝區溢出漏洞時同樣,應該監控應用程序響應中出現的反常事件。

1四、攻擊應用程序架構

當評估某個應用程序的安全狀態時,Web應用程序架構常常被忽略,但實際上它是一個重要的安全領域。在經常使用的分層架構中,若是沒法隔離不一樣的層次,攻擊者就能夠利用某個層次中的一個漏洞徹底攻破其餘層次,進而控制整個應用程序。

14.1.1  攻擊分層架構

設計不佳的分層架構可能受到如下三種類型的攻擊:

(1)能夠利用不一樣層之間的信任關係擴大攻擊範圍,從一個層侵入到另外一個層

(2)若是不一樣層之間沒有徹底隔離,就能夠利用某一層存在缺陷直接破壞另外一層實施的安全保護

(3)局部攻破一個層後,就能夠直接攻擊其餘層的基礎架構,從而將攻擊擴大到其它層。

滲透測試步驟:

(1)對於在應用程序中已肯定的任何漏洞,應發揮想象,考慮如何利用這個漏洞實現滲透測試目標;這是貫穿全書的主題。無數針對Web應用程序實施的成功攻擊,最初都是從利用一個內部影響有限的漏洞開始的。

(2)若是能再任何應用程序組件上執行任意命令,並可以與其餘主機創建網絡鏈接,考慮向網絡與操做系統層面中的應用程序其餘基礎架構發動直接購機,以擴大攻擊範圍。

14.1.2  保障分層架構的安全

若是以嚴謹的方式執行多層架構,該架構就能夠顯著提升應用程序的安全,由於它可以將一次成功攻擊的影響控制在局部。在前面描述的基本LAMP配置中,全部組件都在一臺計算機上運行,攻破其中一個層就可能致使整個應用程序被徹底攻破。在更安全的架構中,攻擊者攻破一個層,只能部分控制應用程序的數據與處理操做,於是其形成的影響有限,可能侷限於被攻破的層中。

(1)儘可能減小信任關係

     應用程序服務器應對特殊的資源與URL路徑實施基於角色的訪問控制。

     數據庫服務器層能夠爲應用程序的不一樣用戶和操做提供各類權限的帳戶。

     全部應用程序組件可使用擁有正常操做所需的最低權限的操做系統帳戶運行。

(2)隔離不一樣的組件

一個層不得讀取或寫入其餘曾使用的文件

對不一樣基礎架構組件之間的網絡級訪問進行過濾,僅容許須要與不一樣應用程序層彼此通訊的服務器

(3)應用深層防護

應根據配置與漏洞補丁,把每臺主機上的技術棧的各個層面進行安全強化

應對保存在任何應用程序層中的數據進行加密,以防止攻破該層的攻擊者輕鬆得到這些數據。

14.2   共享主機與應用程序服務提供商

許多組織經過外部提供商向公衆提供他們的Web應用程序,這些服務器包括組織經過其訪問Web與數據庫服務器的簡單主機服務,以及表明組織主動維護應用程序的成熟應用程序服務器提供商。缺少能力與資源部署本身的應用程序的小型企業經常採用這種服務,但一些知名公司有時也使用這些服務來部署特殊的應用程序。

14.2.1  虛擬主機

簡單的共享主機配置中,一臺Web服務器只需支付幾個域名各不相同的虛擬Web站點。它經過HOST消息頭達到這個目的;在HTTP1.1中,請求中必須包含該消息頭。當瀏覽器提出一個HTTP請求時,請求中既包含一個HOST消息頭,該消息頭中含有相關URL中的域名;而後,請求被傳送到與域名關聯的IP地址中。若是解析幾個域名獲得相同的IP地址,在這個地址上的服務器仍然可以肯定請求但願訪問哪個Web站點。

14.2.2  共享的應用程序服務

許多ASP提供現成的應用程序,可由客戶修改與定製使用。對於擁有大量業務、須要部署功能強大的、能爲終端用戶提供基本相同功能應用程序的行業,使用這種模型能夠節省大量成本。

滲透測試步驟:

(1)檢查爲共享環境中的客戶提供的、便於他們更新和管理內容與功能的訪問機制。考慮如下幾個問題。

     遠程訪問機制是否使用一個安全的協議與通過適當強化的基礎架構?

     客戶是否可以訪問他們正常狀況下不能訪問的文件、數據及其餘資源?

     客戶是否可以在主機環境中得到一個交互式的shell,並執行任意命令?

(2)若是使用一個全部權應用程序,以方便客戶配置和定製共享環境,考慮是否可以以這個應用程序爲攻擊目標,攻破該環境自己及其中運行的全部應用程序

(3)若是可以在某個應用程序中執行命令、注入SQL腳本或訪問任意文件,仔細研究,看是否可以以此擴大攻擊範圍,攻破其餘應用程序

(4)若是滲透測試員正在攻擊一個使用ASP主機的應用程序,且該應用程序由許多共享與定製組件構成,肯定其中的任何共享組件,如日誌機制、管理功能以及數據庫代碼組件,常使用利用這些組件攻破應用程序的共享部分,進而攻破其餘應用程序。

(5)若是全部共享環境使用一個經常使用的數據庫,使用NGSSquirrel之類的數據庫掃描工具,對數據庫配置、補丁版本、表結構以及許可進行全面審查。數據庫安全模型中存在的任何缺陷均可以被加以利用,將攻擊範圍由一個應用程序擴大到另外一個應用程序。

14.2.4  保障共享環境的安全

因爲使用相同工具的客戶懷有惡意企圖以及不知情的客戶在環境中引入的漏洞,所以,共享環境給應用程序安全帶來了新的威脅。爲解決這種雙重威脅,在設計共享環境中必須仔細處理客戶訪問、隔離與信任關係,並實施並不直接適用於單主機應用程序的控制。

(1)保障客戶訪問的安全

(2)隔離客戶功能,不能信任共享環境中的客戶,認爲他們僅建設沒有漏洞的無害功能。

(3)隔離共享應用程序中的組件。

相關文章
相關標籤/搜索