Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    86369495

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

我會在本文中列出一系列可用於繞過行業領先的企業終端保護解決方案的技術。出於安全的考慮,所以我決定不公開發布源代碼。

在模擬攻擊中,“初始訪問”階段的一個關鍵挑戰是繞過企業終端上的檢測和響應能力(EDR)。商業命令和控制框架向紅隊操作員提供不可修改的shellcode 和二進製文件,這些文件由終端保護行業大量簽名,為了執行該植入,該shellcode 的簽名(靜態和行為)需要被混淆。

我會在將介紹以下技術,其最終目的是執行惡意的shellcode,也被稱為(shellcode)加載程序:

Shellcode 加密;

減少熵;

退出(本地)反病毒沙箱;

導入表混淆;

禁用Windows 事件跟踪(ETW);

規避常見的惡意API 調用模式;

直接系統調用和規避“系統調用標記”;

刪除ntdll.dll 中的掛鉤;

欺騙線程調用堆棧;

信標的內存加密;

自定義反射加載程序;

可擴展配置文件中的OpSec 配置;

1. Shellcode加密讓我們從靜態shellcode 混淆話題開始。在我的加載程序中,我利用了XOR 或RC4 加密算法,因為它易於實現並且不會留下大量加載程序執行的加密活動的外部指標。用於混淆shellcode 靜態簽名的AES 加密會在二進製文件的導入地址表中留下痕跡。在此加載程序的早期版本中,我已經讓Windows Defender 專門觸發了AES 解密函數(例如CryptDecrypt、CryptHashData、CryptDeriveKey 等)。

1.png

dumpbin /imports 的輸出,這是二進製文件中僅使用AES 解密函數的痕跡

2. 減少熵許多AV/EDR 解決方案在評估未知二進制時考慮二進制熵。由於我們正在加密shellcode,我們的二進製文件的熵相當高,這清楚地表明二進製文件中的代碼部分被混淆了。

有幾種方法可以減少二進制的熵,兩種簡單的方法是:

2.1 將低熵資源添加到二進製文件中,例如(低熵)圖像。

2.2添加字符串,例如英語詞典或某些“字符串C:\Program Files\Google\Chrome\Application\100.0.4896.88\chrome.dll”輸出。

一個更好的解決方案是設計和實現一種算法,將shellcode 混淆(編碼/加密)成英文單詞(低熵)。

3. 退出(本地)反病毒沙箱許多EDR 解決方案將在本地沙箱中運行二進製文件幾秒鐘以檢查其行為。為了避免對最終用戶體驗的影響,他們檢查二進製文件的時間不能超過幾秒鐘(我曾經見過Avast檢查時間超過30秒,但這是一個例外)。我們可以通過延遲shellcode的執行來濫用這個限制。簡單地計算一個質數是我個人的最愛,並將該數字用作加密shellcode 的(一部分)密鑰。

4.導入表混淆你希望避免可疑的Windows API (WINAPI) 出現在我們的IAT(導入地址表)中。此表包含你的二進製文件從其他系統庫導入的所有Windows API 的概述。可以在此處找到可疑API 列表(因此通常由EDR 解決方案檢查)。通常,這些是VirtualAlloc、VirtualProtect、WriteProcessMemory、CreateRemoteThread、SetThreadContext 等。運行dumpbin /exports

我們添加WINAPI 調用的函數簽名,在ntdll.dll 中獲取WINAPI 的地址,然後創建指向該地址的函數指針:

2.png

使用字符數組混淆字符串會將字符串分割成更小的部分,使它們更難從二進製文件中提取

該調用仍將針對ntdll.dll WINAPI,並且不會繞過ntdll.dll 中WINAPI 中的任何掛鉤,但純粹是為了從IAT 中刪除可疑函數。

5. 禁用Windows 事件跟踪(ETW)許多EDR 解決方案廣泛利用Windows 事件跟踪(ETW),特別是Microsoft Defender for Endpoint(以前稱為Microsoft ATP)。 ETW 允許對進程的功能和WINAPI 調用進行廣泛的檢測和跟踪。 ETW在內核中有一些組件,主要用於註冊系統調用和其他內核操作的回調函數,但也包含一個用戶域組件,它是ntdll.dll (ETW深度攻擊向量)的一部分。由於ntdll.dll是一個加載到二進製文件進程中的DLL,因此我們可以完全控制該DLL,從而控制ETW功能。在用戶空間中,ETW有很多不同的繞過方法,但最常見的是為EtwEventWrite函數打補丁,它被調用來寫入/記錄ETW事件。我們在ntdll.dll中獲取它的地址,並用返回0 (SUCCESS)的指令替換它的第一個指令。

3.png

我發現上述方法仍然適用於兩個測試的EDR,但這是一個嘈雜的ETW 補丁

6. 規避常見的惡意API 調用模式

大多數行為檢測最終都是基於檢測惡意模式。其中一種模式是在短時間內特定的WINAPI調用的順序。第4 部分中簡要提到的可疑WINAPI 調用通常用於執行shellcode,因此受到嚴格監控。然而,這些調用也用於良性活動(VirtualAlloc、WriteProcess、CreateThread 模式結合內存分配和寫入大約250KB 的shellcode),因此使用EDR 解決方案的挑戰是區分良性調用和惡意調用。 你可以參考Filip Olszak 的一篇文章,其中提到利用延遲和較小的分配和寫入內存塊來融入良性WINAPI 調用行為。簡而言之,他的方法調整了典型shellcode 加載程序的以下行為:

6.1 與其分配一大塊內存並直接將大約250KB 的植入shellcode 寫入該內存,不如分配小的連續塊,例如小於64KB 內存並將它們標記為NO_ACCESS。然後以類似的塊大小將shellcode 寫入分配的內存頁面。

6.2 在上述每個操作之間引入延遲。這將增加執行shellcode 所需的時間,但也會使連續執行模式變得不那麼突出。

使用這種技術的一個問題是,要確保在連續的內存頁中找到一個可以容納整個shellcode 的內存位置。 Filip 的DripLoader 實現了這個概念。

我構建的加載程序不會將shellcode 注入另一個進程,而是使用NtCreateThread 在自己的進程空間中的線程中啟動shellcode。未知進程(我們的二進製文件實際上流行率很低)進入其他進程(通常是Windows 原生進程)是突出的可疑活動。當我們在加載程序進程空間的線程中運行shellcode 時,更容易混入進程中良性線程執行和內存操作的噪音。然而,不利的一面是任何崩潰的開發後模塊也會導致加載程序的進程崩潰,從而導致植入程序崩潰。持久性技術以及運行穩定可靠的BOF 可以幫助克服這一缺點。

7. 直接系統調用和迴避“系統調用標記”加載程序利用直接系統調用繞過EDR 放入ntdll.dll 的任何掛鉤。我不想在此過多地討論直接系統調用的話題。

簡而言之,直接系統調用是直接對內核系統調用等效的WINAPI 調用。我們不調用ntdll.dll VirtualAlloc,而是調用它在Windows 內核中定義的內核等效NtAlocateVirtualMemory。我們可以使用這種辦法繞過任何用於監視調用(在本例中)ntdll.dll中定義的VirtualAlloc的EDR掛鉤。

為了直接調用系統調用,我們從ntdll.dll 中獲取我們要調用的系統調用的syscall ID,使用函數簽名將函數參數的正確順序和類型推送到堆棧,然後調用syscall id指令。在此推薦兩個工具,SysWhispers2 和SysWhisper3 就是兩個很好的例子。從規避的角度來看,調用直接系統調用有兩個問題:

7.1 你的二進製文件最終具有syscall 指令,該指令很容易靜態檢測。

7.2 與通過等效的ntdll.dll 調用的系統調用的良性使用不同,系統調用的返回地址不指向ntdll.dll。相反,它指向我們調用系統調用的代碼,它駐留在ntdll.dll 之外的內存區域中。這是一個未通過ntdll.dll調用的系統調用的指標,非常可疑。

為了克服這些問題,我們可以做以下工作:

7.3 實現一個尋蛋機制。將系統調用指令替換為egg(一些隨機的唯一可識別模式),在運行時,在內存中搜索這個egg,並使用ReadProcessMemory和WriteProcessMemory的WINAPI調用將其替換為系統調用指令。然後,我們可以正常地使用直接系統調用。該技術已由klezVirus實現。

7.4我們不是從我們自己的代碼中調用syscall 指令,而是在ntdll.dll 中搜索syscall 指令,並在我們準備好調用系統調用的堆棧後跳轉到該內存地址。這將導致RIP 中的返回地址指向ntdll.dll 內存區域。

這兩種技術都是SysWhisper3 的一部分。

8.刪除ntdll.dll中的掛鉤逃避ntdll.dll 中的EDR 掛鉤的另一個好方法是用來自ntdll.dll 的新副本覆蓋默認加載(並由EDR 掛鉤)加載的ntdll.dll。 ntdll.dll 是任何Windows 進程加載的第一個DLL。 EDR 解決方案確保他們的DLL 在不久之後被加載,這在我們自己的代碼執行之前將所有掛鉤放在加載的ntdll.dll 中。如果我們的代碼之後在內存中加載一個新的ntdll.dll 副本,那些EDR 掛鉤將被覆蓋。 RefleXXion 是一個C++ 庫,它實現了MDSec 對該技術所做的研究。 RelfeXXion 使用直接系統調用NtOpenSection 和NtMapViewOfSection 來獲取\KnownDlls\ntdll.dll(具有先前加載的DLL 的註冊表路徑)中乾淨ntdll.dll 的句柄。然後它會覆蓋加載的ntdll.dll 的.TEXT 部分,從而清除EDR 掛鉤。

我建議使用與第7部分中描述的相同的方法來調整RefleXXion庫。

9. 欺騙線程調用棧接下來的兩部分將介紹兩種技術,它們可以避免在內存中檢測我們的shellcode。由於植入程序的信標行為,大部分時間植入程序都處於休眠狀態,等待其操作員的傳入任務。在此期間,植入程序容易受到來自EDR 的內存掃描技術的攻擊。本文中描述的兩種規避方法中的第一種就是欺騙線程調用堆棧。

當植入程序處於休眠狀態時,它的線程返回地址指向我們駐留在內存中的shellcode。通過檢查可疑進程中線程的返回地址,可以很容易地識別出我們的植入shellcode。為了避免這種情況,想打破返回地址和shellcode之間的這種聯繫。我們可以通過掛鉤Sleep() 函數來做到這一點。當該掛鉤被調用(通過植入/信標shellcode)時,我們用0x0 覆蓋返回地址並調用原始的Sleep() 函數。當Sleep() 返回時,我們將原始返回地址放回原處,以便線程返回到正確的地址以繼續執行。 Mariusz Banach 在他的ThreadStackSpoofer 項目中實現了這種技術。

在下面的兩個屏幕截圖中,我們可以觀察到欺騙線程調用堆棧的結果,其中非欺騙的調用堆棧指向非備份內存位置,而欺騙的線程調用堆棧指向掛鉤的Sleep (MySleep)函數,並“切斷”調用堆棧的其餘部分。

4.png

默認信標線程調用堆棧

5.png

欺騙信標線程調用堆棧

10.信標內存加密另一個規避內存檢測的方法是在休眠時加密植入程序的可執行內存區域。使用與上一節中描述的相同的sleep 掛鉤,我們可以通過檢查調用者地址(調用Sleep() 的信標代碼以及我們的MySleep() 掛鉤)來獲取shellcode 內存段。如果調用者內存區域是MEM_PRIVATE 和EXECUTABLE ,並且大小與我們的shellcode差不多,那麼內存段將使用XOR 函數加密並調用Sleep()。然後Sleep() 返回,它解密內存段並返回給它。

另一種技術是註冊一個vector Exception Handler (VEH),它處理NO_ACCESS違規異常,解密內存段並更改RX的權限。然後就在休眠之前,將內存段標記為NO_ACCESS,這樣當Sleep()返回時,就會出現內存訪問衝突異常。因為我們註冊了一個VEH,所以異常是在該線程上下文中處理的,並且可以在引發異常的完全相同的位置恢復。 VEH 可以簡單地解密並將權限更改回RX,並且植入程序可以繼續執行。這種技術可以防止在植入程序處於睡眠狀態時出現可檢測的Sleep() 掛鉤。

Mariusz Banach 也在ShellcodeFluctuation 中實現了這種技術。

11.自定義反射加載程序我們在這個加載程序中執行的信標shellcode 最終是一個需要在內存中執行的DLL。許多C2 框架利用Stephen Fewer 的ReflectiveLoader。關於反射性DLL 加載程序的工作原理有很多書面解釋,Stephen Fewer 的代碼也有很好的文檔記錄,但簡而言之,反射式加載程序執行以下操作:

11.1將地址解析為加載DLL 所需的必要kernel32.dll WINAPI(例如VirtualAlloc、LoadLibraryA 等);

11.2將DLL 及其部分寫入內存;

11.3建立DLL 導入表,使DLL 可以調用ntdll.dll 和kernel32.dll WINAPI;

11.4加載任何其他庫並解析它們各自的導入函數地址;

11.5調用DLL 入口點;

Cobalt Strike 添加了對在內存中反射加載DLL 的自定義方式的支持,允許攻擊人員自定義加載信標DLL 的方式並添加規避技術。 Bobby Cooke 和Santiago P 使用我在裝載機中使用的Cobalt Strike 的UDRL 構建了一個隱形裝載機(BokuLoader)。 BokuLoader 實現了幾種規避技術:

11.6 限制對GetProcAddress() 的調用(通常是EDR 掛鉤WINAPI 調用來解析函數地址,就像我們在第4 部分中所做的那樣);

11.7AMSI和ETW繞過;

11.8 僅使用直接系統調用;

11.9僅使用RW 或RX,沒有RWX (EXECUTE_READWRITE) 權限;

11.10 從內存中刪除信標DLL 標頭;

請確保取消這兩個定義的註釋,以利用通過HellsGate和HalosGate的直接系統調用,並繞過ETW和AMSI(其實沒有必要,因為我們已經禁用了ETW,並且沒有將加載程序注入到另一個進程中)。

12. Malleable 配置文件中的OpSec 配置在你的Malleable C2 配置文件中,確保配置了以下選項,這些選項限制了RWX標記內存的使用(可疑且容易檢測),並在信標啟動後清理shellcode。

6.png

總結結合這些技術,你可以繞過Microsoft Defender for Endpoint 和CrowdStrike Falcon,且不會被檢測到。

7.png

CrowdStrike Falcon 上的測試