檢測Cobalt Strike只使用40行代碼
無文件落地的木馬主要是一段可以自定位的shellcode組成,特點是沒有文件,可以附加到任何進(jìn)程里面執(zhí)行。一旦特征碼被捕獲甚至是只需要xor一次就能改變特征碼,由于傳統(tǒng)安全軟件是基于文件檢測的,對目前越來越多的無文件落地木馬檢查效果差。
基于內(nèi)存行為特征的檢測方式,可以通過檢測執(zhí)行代碼是否在正常文件鏡像區(qū)段內(nèi)去識別是否是無文件木馬.由于cobaltstrike等無文件木馬區(qū)段所在的是private內(nèi)存,所以在執(zhí)行l(wèi)oadimage回調(diào)的時候可以通過堆棧回溯快速確認(rèn)是否是無文件木馬。
檢測只需要40行代碼:
- 在loadimagecallback上做堆棧回溯
- 發(fā)現(xiàn)是private區(qū)域的內(nèi)存并且是excute權(quán)限的code在加載dll,極有可能,非常有可能是無文件木馬或者是shellcode在運(yùn)行:
- void LoadImageNotify(PUNICODE_STRING pFullImageName, HANDLE pProcessId, PIMAGE_INFO pImageInfo)
- {
- UNREFERENCED_PARAMETER(pFullImageName);
- UNREFERENCED_PARAMETER(pProcessId);
- UNREFERENCED_PARAMETER(pImageInfo);
- if (KeGetCurrentIrql() != PASSIVE_LEVEL)
- return;
- if (PsGetCurrentProcessId() != (HANDLE)4 && PsGetCurrentProcessId() != (HANDLE)0) {
- if (WalkStack(10) == false) {
- DebugPrint("[!!!] CobaltStrike Shellcode Detected Process Name: %s\n", PsGetProcessImageFileName(PsGetCurrentProcess()));
- ZwTerminateProcess(NtCurrentProcess(), 0);
- return;
- }
- }
- return;
- }
堆?;厮荩?/p>
- bool WalkStack(int pHeight)
- {
- bool bResult = true;
- PVOID dwStackWalkAddress[STACK_WALK_WEIGHT] = { 0 };
- unsigned __int64 iWalkChainCount = RtlWalkFrameChain(dwStackWalkAddress, STACK_WALK_WEIGHT, 1);
- int iWalkLimit = 0;
- for (unsigned __int64 i = iWalkChainCount; i > 0; i--)
- {
- if (iWalkLimit > pHeight)
- break;
- iWalkLimit++;
- if (CheckStackVAD((PVOID)dwStackWalkAddress[i])) {
- DebugPrint("height: %d address %p \n", i, dwStackWalkAddress[i]);
- bResult = false;
- break;
- }
- }
- return bResult;
- }
使用:
編譯好驅(qū)動,加載驅(qū)動,之后運(yùn)行測試看看:
普通生成(x32與x64)測試:
基于VirtualAlloc的C代碼測試:
測試結(jié)果:
基于powershell的測試:
基于python的測試:
測試結(jié)果:
弊端:
目前已知的ngentask.exe、sdiagnhost.exe服務(wù)會觸發(fā)這個檢測規(guī)則(看樣子是為了執(zhí)行一些更新服務(wù)從微軟服務(wù)端下載了一些shellcode之類的去運(yùn)行).如果后續(xù)優(yōu)化則需要做一個數(shù)字簽名校驗等給這些特殊的進(jìn)程進(jìn)行加白操作.這是工程問題,不是這個demo的問題
一如既往的 github:https://github.com/huoji120/CobaltStrikeDetected