引言
本文主要介紹“反射型 dll 注入”及“柔性加載”技術(shù)。
反射型 dll 注入
為什么需要反射型 dll 注入
常規(guī)的 dll 注入代碼如下:
int main(int argc, char *argv[]) {
HANDLE processHandle;
PVOID remoteBuffer;
wchar_t dllPath[] = TEXT("C:\\experiments\\evilm64.dll");
printf("Injecting DLL to PID: %i\n", 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 數(shù)組
-
將該 payload 數(shù)組寫入目標(biāo)內(nèi)存
-
在目標(biāo)內(nèi)存中找到 LoadLibraryW 函數(shù)
-
通過 CreateRemoteThread 調(diào)用 LoadLibraryW 函數(shù),參數(shù)為 dll 在內(nèi)存中的地址。
這樣的操作模式有幾個(gè)很高危的點(diǎn)。首先,從磁盤讀取 dll 需要考慮 dll 的靜態(tài)免殺,對此我們可以直接寫在裝載器中并加密。
其次,在目標(biāo)內(nèi)存中找到 LoadLibraryW 函數(shù),需要 GetProcAddress LoadLibraryW,這種調(diào)用屬于很有特征的調(diào)用模式,容易被 AV/EDR 歸類。對此我們的解決措施就是接下來要提及的反射型 dll 注入技術(shù)。
最后,CreateRemoteThread 進(jìn)行遠(yuǎn)程線程注入 行為本身就很高危,同時(shí)參數(shù)是 LoadLibraryW 的地址,一眼 malware。
對此我們優(yōu)化調(diào)用,不再使用 CreateRemoteThread 進(jìn)而使用創(chuàng)建新進(jìn)程的方式結(jié)合反射型 dll 注入技術(shù)改變 dll 注入技術(shù)的調(diào)用模式。
實(shí)現(xiàn)思路
早期的 dll 注入實(shí)現(xiàn)原理:
上圖比較清楚的寫了反射型 dll 注入的原理,1,2,3 步由 A 向 B 線程寫入 dll。第四步調(diào)用 B 線程中的 embedded bootstrapper code。最后通過 bootstrapper shellcode 調(diào)用 dll 的導(dǎo)出函數(shù) reflective loader。
reflective loader 實(shí)際上是一個(gè)自己實(shí)現(xiàn)的 LoadLibraryW 函數(shù),從內(nèi)存中找到我們寫入的 dll 并修復(fù)使其成為可以被正常使用的 pe 文件,最后調(diào)用 DLLmain 實(shí)現(xiàn)我們的惡意功能。
我們的具體實(shí)現(xiàn)和上面早期的思路有所區(qū)別,首先我們不使用遠(yuǎn)程進(jìn)程/線程注入的方式,其次我們不需要 bootstrapper shellcode 這個(gè)部分,我們可以直接在加載器部分算出 reflective loader 在內(nèi)存中的地址,直接調(diào)用即可。
【一一幫助安全學(xué)習(xí),所有資源獲取處一一】
①網(wǎng)絡(luò)安全學(xué)習(xí)路線
②20 份滲透測試電子書
③安全攻防 357 頁筆記
④50 份安全攻防面試指南
⑤安全紅隊(duì)滲透工具包
⑥網(wǎng)絡(luò)安全必備書籍
⑦100 個(gè)漏洞實(shí)戰(zhàn)案例
⑧安全大廠內(nèi)部視頻資源
⑨歷年 CTF 奪旗賽題解析
具體實(shí)現(xiàn)
加載器部分
首先 shellcode 使用 AES 解密,這部分添加了一些 c 的代碼加密
后來發(fā)現(xiàn)原本項(xiàng)目的 release 目錄下有 python 的加密腳本:
解密載入內(nèi)存后,使用 GetReflectiveLoaderOffset 計(jì)算出 ReflectLoader 函數(shù)的偏移:
最后創(chuàng)建線程調(diào)用 ReflectLoader 函數(shù)。
dll 部分
ReflectiveLoader 一共做了 5 件事:
一、 解析加載 DLL 所需 kernel32.dll WINAPI 的地址(例如 VirtualAlloc, LoadLibraryA 等),通過關(guān)鍵函數(shù)的 hash 在內(nèi)存中搜索,函數(shù) hash:
遍歷內(nèi)存進(jìn)行搜索:
二、 將 DLL 及其相應(yīng)的節(jié)寫入內(nèi)存中:
三、 建立 DLL 導(dǎo)入表,以便 DLL 可以調(diào)用 ntdll.dll 和 kernel32.dll WINAPI
四、 修復(fù)重定位表:
五、 調(diào)用 DLL 的入口點(diǎn):
最終我們的惡意代碼位于 dllmain 中,項(xiàng)目還是采用加載 shellcode 的方式上線 cs。
柔性加載
限制使用具有 RWX 標(biāo)記的內(nèi)存,cs 在 4+可以直接進(jìn)行相關(guān)配置。
推薦配置:
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
加強(qiáng) shellcode 的混淆:
std::string rest2_reference = "xxx@@";
std::string rest3_reference = replace(rest2_reference, "@@", "==");
依舊報(bào)毒,但是類型發(fā)生改變了,說明靜態(tài)的混淆有效果:
異或的操作,比較可疑,經(jīng)過測試發(fā)現(xiàn)是 cs 的 shellcode 出現(xiàn)在數(shù)組里就報(bào)毒,應(yīng)該是對內(nèi)存進(jìn)行的掃描。
所以我們可以使用《文章二》中提及的技術(shù)“規(guī)避常見的惡意 API 調(diào)用模式”,將 shellcode 分片直接寫入連續(xù)內(nèi)存。
在測試的過程中發(fā)現(xiàn)莫名其妙的過了查殺:
很神奇,這段并沒有實(shí)現(xiàn)內(nèi)存的切片寫入,因?yàn)?shellcode 的大小沒有達(dá)到 4096,實(shí)際上相當(dāng)于直接分配了個(gè)大小為 4096 的數(shù)組,寫入了 shellcode。
而且把這段代碼相同的格式放外面就不行,個(gè)人感覺 definder 還是沒有去檢查內(nèi)存。
可能是有語義分析的引擎,這次剛好繞過了語義分析。
macfee
同上方法可以成功 bypass:
正常執(zhí)行命令:
kasperky Endpoint 11 for windows
用過 macfee 和 definder 的 demo2 測試失敗,注釋掉代碼加載部分不報(bào)毒,改用 apc 和創(chuàng)建進(jìn)程的的方式加載內(nèi)存:
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 調(diào)用 NtCreateThreadEx。這里被坑了,WaitForSingleObject 要使用,不然會異步,沒法上線:
ANtCTE(
&hThread,
THREAD_ALL_ACCESS,
NULL,
GetCurrentProcess(),
(LPTHREAD_START_ROUTINE)exec,
NULL,
NULL,
0,
0,
0,
nullptr
);
WaitForSingleObject(hThread, INFINITE);
能看到效果,行為檢測依舊有問題:
但漏洞利用防御已經(jīng)沒有相關(guān)報(bào)警:
懷疑是 cs 本身流量特征的問題,為了驗(yàn)證我使用卡巴斯基本身的功能禁用了網(wǎng)絡(luò)請求:
確實(shí)不殺也不報(bào)警了,確定是 cs 通信的問題。
ESET Endpoint Security
demo3 報(bào)警,并且明顯檢測到網(wǎng)絡(luò)連接行為
靜態(tài)沒有問題
主要應(yīng)該還是在對內(nèi)存的檢測,而且感覺已經(jīng)執(zhí)行到了發(fā)包
下面根據(jù)《三》中的“beacon 的內(nèi)存加密”對 demo3 進(jìn)行優(yōu)化,使用 RefleXXion 工具的第二種將內(nèi)存設(shè)為 NO_ACCESS 并通過注冊異常處理還原的方式進(jìn)行免殺。
設(shè)置流量的白名單:
關(guān)閉 web 控制后成功并上線
eset 在持續(xù)在掃描內(nèi)存,但一直沒有權(quán)限,一直觸發(fā)異常,無法進(jìn)入正常的后門邏輯
能繞過內(nèi)存的檢測,但無法正常使用
感覺 ESET 一直在我程序里進(jìn)行內(nèi)存操作,訪問到了不可訪問的內(nèi)存段。
可能 ESET 的機(jī)制是一直在掃描程序內(nèi)存,也可能是想要做一些 hook。
我嘗試使用 RefleXXion 的第一種方法,將 shellcode 加密并使屬性為 RW 或 RX 的方式加載 shellcode:
可以成功上線,并且正常使用:
文章來源:http://www.zghlxwxcb.cn/news/detail-430287.html
總結(jié)
該系列文章所有的 bypass edr 方法都只在用戶態(tài)進(jìn)行操作,已經(jīng)能規(guī)避大多數(shù) AV/EDR 的檢測。但不乏一些 edr 進(jìn)行了比較多的內(nèi)核層面的限制,如炭黑、fireeye 等。文章來源地址http://www.zghlxwxcb.cn/news/detail-430287.html
到了這里,關(guān)于【網(wǎng)絡(luò)安全】紅隊(duì)基礎(chǔ)免殺的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!