English:https://outflank.nl/blog/2019/06/19/red-team-tactics-combining-direct-system-calls-and-srdi-to-bypass-av-edr/python
在本文咱們將介紹如何使用直接系統調用(Direct System Calls)以及配合sRDI注入來繞過R3層的行爲監控。git
隨着安全技術的防護能力逐漸加強,另外一方面,攻擊技術也在不斷髮展,做爲一個Red Team須要研究更先進的技術來繞過當下比較流行的防護和檢測機制。程序員
近期一篇惡意代碼的研究報告聲稱,使用"直接系統調用"技術來繞過安全軟件用戶層Hook的惡意樣本正在與日俱增。github
研究報告:https://www.cyberbit.com/blog/endpoint-security/malware-mitigation-when-direct-system-calls-are-used/shell
做爲一名ReadTeamer,要與時俱進!! 如今輪到咱們也來更新一波shellcode攻擊代碼了。編程
咱們將接下來將使用這種技術證實,在不觸碰磁盤的狀況下繞過AV/EDR監控的用戶層Hook,使用Cobalt Strike來dump LSASS.exe進程內存。sass
PoC代碼能夠在這裏下載:https://github.com/outflanknl/Dumpert安全
爲了弄清楚直接系統調用的真正含義,首先咱們須要先深刻Windows操做系統底層架構。架構
若是你使用過MS-DOS年代Windows系統,也許你會記得,一個簡單的程序崩潰能夠引發整個操做系統癱瘓。dom
這是由於操做系統在實模式(Real Mode)運行,處理器在實模式下運行時,不會有內存隔離的概念(沒有嚴格限制或者聲明,哪些內存區域是能夠訪問,哪些不能訪問)。
也就意味者,若是你寫的程序出現了bug致使內存破壞(Memory Currption)會致使整個操做系統中止運行。
一直到後來出現了能夠支持保護模式的新處理器和操做系統,這一現象才被改變。
爲了防止一個進程崩潰致使操做系統也跟着崩潰,在保護模式下引入了許多安全措施,經過虛擬內存(Virtual Memory)和權限級別(Privilege Levels),和一個叫Rings的概念,來隔離運行的不一樣進程之間,以及進程和操做系統之間的內存訪問。
Rings一共有4層,Ring0 ~ Ring3分別對應4個特權級別。
Windows操做系統中實際只使用了兩個特權級別:
一個是Ring3層,平時咱們所見到的應用程序運行在這一層,因此叫它用戶層,也叫User-Mode。因此下次聽到別人講(Ring三、用戶層、User-Mode)時,實際上是在講同一個概念。
一個是Ring0層,像操做系統內核(Kernel)這樣重要的系統組件,以及設備驅動都是運行在Ring0,內核層,也叫Kernel-Mode。
經過這些保護層來隔離普通的用戶程序,不能直接訪問內存區域,以及運行在內核模式下的系統資源。
當一個用戶層程序須要執行一個特權系統操做,或者訪問內核資源時。處理器首先須要切換到Ring0模式下才能執行後面的操做。
切換Ring0的代碼,也就是直接系統調用所在的地方。
咱們經過監控Notepad.exe進程保存一個.txt文件,來演示一個應用層程序如何切換到內核模式執行的:
上面截圖展現了Notepad.exe進程保存一個文件時的執行流程(call stack),從下往上看執行流程。
咱們能夠看到 notepad調用了kernel32模塊中的WriteFile 函數,而後該函數內部又調用了ntdll中的NtWriteFile來到了Ring3與Ring0的臨界點。
由於程序保存文件到磁盤上,因此操做系統須要訪問相關的文件系統和設備驅動。應用層程序本身是不容許直接訪問這些須要特權資源的。
應用程序直接訪問設備驅動會引發一些意外的後果(固然操做系統不會出事,最多就是應用程序的執行流程出錯致使崩潰)。因此,在進入內核層以前,調用的最後一個用戶層API就是負責切換到內核模式的。
CPU中經過執行syscall指令,來進入內核模式,至少x64架構是這樣的。咱們能夠經過下面 WinDBG截圖中看到,反彙編的NtWriteFile指令:
把被調用函數相關的參數PUSH到棧上之後,ntdll中的NtWriteFile函數的職責就是,設置EAX爲對應的"系統調用號",最後執行syscall指令,CPU就來到了內核模式(Ring0)下執行。
進入內核模式後,內核經過diapatch table(SSDT),來找到和系統調用號對應的Kernel API,而後將用戶層棧上的參數,拷貝到內核層的棧中,最後調用內核版本的ZwWriteFile函數。
當內核函數執行完成時,使用幾乎相同的方法回到用戶層,並返回內核API函數的返回值(指向接收數據的指針或文件句柄)。
像NtWriteFile這樣在進入內核層以前的函數,通常也是大部分安全產品,好比:AV、EDR和Sanbox軟件常常設置Hook的地方,它們經過Inline Hook來劫持執行流程到本身引擎中,以便完成對一些敏感API的監控,
若是發現任何可疑的參數,則直接返回失敗,或彈出窗口警告。
正如上圖NtWriteFile函數的反彙編指令,你可能注意到了,它只有短短8行彙編指令,在這些指令中最重要的就是:系統調用號、syscall指令、進入NtWriteFile函數前,PUSH到棧上的正確的參數,以及使用正確的調用約定。
有了上面這些知識的鋪墊,咱們爲什麼不本身來實現這幾行彙編代碼,模擬直接系統調用(Direct System Calls)就能夠不用再調用NTDLL中的任何函數了,同時咱們也Bypass了User-Mode(Ring3)下面任何設置在NTDLL函數中的Hook。
這正是本文的目的,在開始動手以前,咱們先來簡單瞭解一下Windows編程接口。
用戶層的應用程序要想和底層系統交互,一般使用應用程序編程接口(Application Programming Interface )也就是所謂的API。若是你是編寫C/C++應用的Windows程序開發程序員,一般使用 Win32 API。
Win32API是微軟封裝的一套API接口,由幾個DLL(所謂的Win32子系統DLL)組成。在Win32 API下面使用的是Naitve API(ntdll.dll),這個纔是真正用戶層和系統底層交互的接口,通常稱爲用戶層和內核層之間的橋樑。
可是ntdll中函數大部分都沒有被微軟記錄到官方的開發文檔中,爲了兼容性問題,大多數狀況在寫程序時,應該避免直接使用ntdll中的API。
微軟在Native API上面又封裝一層的神奇之處正是由於Native API是用戶層與內核層之間的橋樑,這樣就能夠在不影響Win32編程接口的狀況下對系統結構進行修改。
如今咱們對系統調用和Windows編程API有了一些瞭解,讓咱們看看如何經過編程來繞過Win32接口層,直接調用系統API並繞過潛在的Ring3層Hook。
有一個小問題尚未提到,系統調用號會受OS版本影響而變化,有時甚至是Service Pack、內置版本號等。不過不用擔憂,Google Project Zero項目的 @j00ru 成員統計了全部Windows系統版本中的Native API的系統調用號。
在線查詢系統調用號:https://j00ru.vexillium.org/syscalls/nt/64/ 有了這張表,咱們能夠直接搜索咱們想要使用的Native API,就能夠看到該API在不一樣系統中的調用號。
咱們須要編寫彙編來調用Driect System Calls。 在Virtual Studio項目中須要啓用MASM編譯依賴的支持,咱們才能在項目中添加.asm文件。
要想經過這種方法來編寫一個高級木馬來徹底繞過用戶層API調用幾乎是不可能的,至少實現起來很是麻煩,由於這些參數結構的問題、等等。
有時候可能你只是想在惡意代碼中使用一個API函數,可是,未曾想這個API的調用堆棧某處早已被一些AV、EDR軟件設置了Hook,隨時等你上鉤。
讓咱們來看看如何使用直接系統調用來卸載Hook。
一般狀況下,基於用戶模式的AV、EDR軟件經過使用跳轉指令(JMP),將API入口處前5個字節修改成指向安全軟件的Hook函數。
卸載這種Hook的方法也早被 @SpecialHoang 和@domchell這兩位大神公佈過了: https://www.mdsec.co.uk/2019/03/silencing-cylance-a-case-study-in-modern-edrs/
若是你仔細研究這些卸載Hook的思路,你會注意到這些方法中用到了諸如:VirtualProtectEx、WriteProcessMemory之類的API來卸載Native API函數的Hook。
可是若是VirtualProtectEx這些API也被Hook和監視了呢?嘿嘿!這下咱們就能夠經過直接系統調用來卸載這些Hook,不怕半路殺出個程咬金了。
在咱們的PoC代碼中,基本上和普通卸載Hook的思路同樣,恢復被Hook函數的前5字節原始的彙編指令代碼。惟一的區別是咱們執行恢復時調用的是Direct System Calls函數(ZwProtectVirtualMemory 和ZwWriteVirtualMemory)。
若是你正在進行評估,而且你的攻擊場景須要儘量保持隱蔽,直接在終端上使用Mimikatz並非最好的方法(即便是在內存中)。另外使用procdump等工具轉儲LSASS內存一般會被 AV、EDR的Hooks檢測到。
所以咱們須要一個替代方案來訪問LSASS內存,@SpecialHoang 博客中公佈了一個方法,先卸載相關函數的Hook,而後再建立LSASS的內存轉儲。
做爲概念證實,咱們建立了一個名爲」Dumpert「的LSASS內存轉儲工具。此工具結合了直接系統調用和卸載API Hook,可讓你建立一個LSASS的minidump。而且可能會Bypass一些AV、EDR產品的檢測。
獲得Minidump文件後,咱們就能夠在本身機器上使用Mimikatz來提取憑證信息。
若是咱們不想磁盤落地,就得須要使用某種注入技術。咱們能夠寫一個反射式加載的DLL,可是反射式DLL注入會留下能夠被檢測到的內存數據。
個人同事@StanHacked告訴我一種稱爲 "Shellcode Reflective DLL Injection"的DLL注入技術。
sRDI能夠把一個普通的DLL文件轉換爲一段不依賴任何位置的Shellcode,這項技術是被Slient Break Security的Nick Landers(@monoxgas)開發,基本上屬於RDI的升級版。
相對於標準RDI,使用SRDI的一些優勢:
想了解更多sRDI細節的朋友,能夠參考這篇文章:https://silentbreaksecurity.com/srdi-shellcode-reflective-dll-injection
爲了讓這一步更方便,咱們提供了一個aggressor腳本:https://github.com/outflanknl/Dumpert/tree/master/Dumpert-Aggressor,啓用腳本之後,能夠在beacon菜單中使用dumpert命令來一鍵搞定!!
可以繞過安全產品Hook的惡意軟件正在逐漸增多,咱們須要在咱們的項目中也嵌入這種技術。
本文中,咱們經過編寫彙編代碼,基於Native API的函數原型、以及不一樣版本的系統調用號,實現了直接系統函數調用。使用這些函數時,就像是在直接使用Native API中的函數同樣。
咱們將這種技術與API卸載Hook結合,實現了從LSASS中建立一個minidump,而且使用了sRDI結合Cobalt Strike來注入dumpert shellcode到目標系統內存中。
檢測惡意使用系統調用比較困難,因爲繞過了用戶模式編程接口,所以惟一能查找惡意行爲的地方只能內核中。可是因爲內核被PatchGuard保護,安全產品更要想在運行的內核中建立鉤子或修改內核文件,就更難了!!!
我但願這篇文章可以幫助理解黑客如今使用的這種高級攻擊技術,但願這篇文章能在Read Team行動中給你們一些有用的啓發!!