官术网_书友最值得收藏!

3.1 shellcode概述

3.1.1 shellcode與exploit

1996年,Aleph One在Underground發表了著名論文Smashing the Stack for Fun and Profit,其中詳細描述了Linux系統中棧的結構和如何利用基于棧的緩沖區溢出。在這篇具有劃時代意義的論文中,Aleph One演示了如何向進程中植入一段用于獲得shell的代碼,并在論文中稱這段被植入進程的代碼為“shellcode”。

后來人們干脆統一用shellcode這個專用術語來通稱緩沖區溢出攻擊中植入進程的代碼。這段代碼可以是出于惡作劇目的的彈出一個消息框,也可以是出于攻擊目的的刪改重要文件、竊取數據、上傳木馬病毒并運行,甚至是出于破壞目的的格式化硬盤等。請注意本章討論的shellcode是這種廣義上的植入進程的代碼,而不是狹義上的僅僅用來獲得shell的代碼。

shellcode往往需要用匯編語言編寫,并轉換成二進制機器碼,其內容和長度經常還會受到很多苛刻限制,故開發和調試的難度很高。

在技術文獻中,我們還會經常看到另一個術語——exploit。

植入代碼之前需要做大量的調試工作,例如,弄清楚程序有幾個輸入點,這些輸入將最終會當作哪個函數的第幾個參數讀入到內存的哪一個區域,哪一個輸入會造成棧溢出,在復制到棧區的時候對這些數據有沒有額外的限制等。調試之后還要計算函數返回地址距離緩沖區的偏移并淹沒之,選擇指令的地址,最終制作出一個有攻擊效果的“承載”著shellcode的輸入字符串。這個代碼植入的過程就是漏洞利用,也就是exploit。

exploit一般以一段代碼的形式出現,用于生成攻擊性的網絡數據包或者其他形式的攻擊性輸入。expliot的核心是淹沒返回地址,劫持進程的控制權,之后跳轉去執行shellcode。與shellcode具有一定的通用性不同,exploit往往是針對特定漏洞而言的。

其實,漏洞利用的過程就好像一枚導彈飛向目標的過程。導彈的設計者關注的是怎樣計算飛行路線,鎖定目標,最終把彈頭精確地運載到目的地并引爆,而并不關心所承載的彈頭到底是用來在地上砸一個坑的鉛球,還是用來毀滅一個國家的核彈頭;這就如同exploit關心的是怎樣淹沒返回地址,獲得進程控制權,把EIP傳遞給shellcode讓其得到執行并發揮作用,而不關心shellcode到底是彈出一個消息框的惡作劇,還是用于格式化對方硬盤的窮兇極惡的代碼,如圖3.1.1所示。

圖3.1.1 緩沖區溢出過程中的功能模塊劃分

隨著現代化軟件開發技術的發展,模塊化、封裝、代碼重用等思想在漏洞利用技術中也得以體現。試想如果仿照武器的設計思想,分開設計導彈和彈頭,將各自的技術細節封裝起來,使用標準化的接口,漏洞利用的過程是不是會更容易些呢?其實在第4章中將介紹到的通用漏洞測試平臺Metasploit就是利用了這種觀點。Metasploit通過規范化exploit和shellcode之間的接口把漏洞利用的過程封裝成易用的模塊,大大減少了expliot開發過程中的重復工作,深刻體現了代碼重用和模塊化、結構化的思想。在這個平臺中:

(1)所有的exploit都使用漏洞名稱來命名,里邊包含有這個漏洞的函數返回地址,所使用的跳轉指令地址等關鍵信息。

(2)將常用的shellcode(例如,用于綁定端口反向連接、執行任意命令等)封裝成一個個通用的模塊,可以輕易地與任意漏洞的exploit進行組合。

題外話:與導彈的比喻不謀而合,在Metasploit中存在漏洞的受害主機會被當作一個叫“target”的選項進行配置,而shellcode同樣也有一個更加形象的名字:payload。不知道在Metasploit以后的版本中會不會把exploit配置改成missile。

3.1.2 shellcode需要解決的問題

2.4節中的代碼植入過程是一個簡化到了極點的實驗。其實,這個實驗中還有一些問題需要進一步完善。

在2.4節的代碼植入實驗中,我們直接用OllyDbg查出了棧中shellcode的起始地址。而在實際調試漏洞時,尤其是在調試IE中的漏洞時,我們經常會發現有缺陷的函數位于某個動態鏈接庫中,且在程序運行過程中被動態裝載。這時的棧中情況將會是動態變化著的,也就是說,這次從調試器中直接抄出來的shellcode起始地址下次就變了。所以,要編寫出比較通用的shellcode就必須找到一種途徑讓程序能夠自動定位到shellcode的起始地址。有關利用跳轉指令定位shellcode的討論將在3.2節中進行。

緩沖區中包括shellcode、函數返回地址,還有一些用于填充的數據。3.3節中將介紹怎樣組織緩沖區內的這些內容。

不同的機器、不同的操作系統中同一個API函數的入口地址往往會有差異。還記得2.4節中我們是怎樣通過Depends獲得MessageBoxA函數入口地址的嗎?直接使用手工查出的API地址的shellcode很可能在調試通過后換一臺計算機就會因為函數地址不同而出錯。為此,我們必須讓shellcode自己在運行時動態地獲得當前系統的API地址。3.4節會帶領您綜合跳轉地址、shellcode的分布、自動獲得API等技術,把2.4節中那段簡陋的shellcode改造成比較通用的版本。在這節的實驗中還將穿插介紹shellcode的調試方法,怎樣從匯編代碼中提取機器代碼等實際操作中將遇到的問題。

3.5節中將著重介紹如何通過使用對shellcode編碼解碼的方法,繞過軟件對緩沖區的限制及IDS等的檢查。

3.6節重點介紹了在整個緩沖區空間有限的情況下,怎樣使代碼更加精簡干練,從而盡量縮短shellcode的尺寸,開發出短小精悍的shellcode。這節中的實驗部分最終只用了191個字節的機器碼就實現了一個把命令行窗口綁定到特定端口的bindshell。

本章前5節的知識是在Windows平臺下開發shellcode的核心知識,也是后續學習的基礎。當然,如果您對shellcode開發技術本身很感興趣,并且有豐富的匯編語言編程經驗,相信3.6節中討論的編程技術對您開發更高級的shellcode一定會有所幫助。

主站蜘蛛池模板: 榆社县| 上饶县| 沽源县| 元阳县| 林西县| 泸定县| 西城区| 石城县| 淮滨县| 崇阳县| 酒泉市| 沈阳市| 闵行区| 德庆县| 巴塘县| 梓潼县| 彰化市| 娱乐| 吉安市| 仪征市| 望奎县| 巴林左旗| 石台县| 高邮市| 高碑店市| 长阳| 托里县| 工布江达县| 怀柔区| 丰都县| 鲁山县| 陈巴尔虎旗| 达尔| 五原县| 会同县| 桦川县| 施甸县| 孝义市| 东乡族自治县| 天门市| 香格里拉县|