百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

CobaltStrike的狩猎与反狩猎 狩猎模型

suiw9 2024-11-11 15:52 21 浏览 0 评论

0x01 前言

又到了xxx的时间了,在对红队基础设施的准备时写下的这篇文章

0x02 开始狩猎

CobaltStrike版本:4.9.1

不做任何配置启动teamserver

使用默认配置的生成x64位beacon,上线pid为3040

0x021 BeaconEye

BeaconEye 的核心原理是通过扫描CobaltStrike中的内存特征,并进行Beacon Config扫描解析出对应的Beacon信息

BeaconEye是基于.NETFramework 4.8框架开发的,至少需要.net4.0以上,为了解决真实环境下低版本服务器没有.net4.0以上的环境,可以使用EvilEye替代BeaconEye,EvilEye是Golang版本的BeaconEye

我目前使用的测试环境为Windows Server 2008,所以直接使用EvilEye进行检测,可以看到能直接从内存中提取出Beacon的信息

0x022 Hunt-Sleeping-Beacons

Hunt-Sleeping-Beacons项目的主要功能是帮助广大研究人员在运行时或其他正在运行进程的上下文场景中识别休眠的Beacon

可以看到Hunt-Sleeping-Beacons可以检测出异常的进程,但是我在实际测试中发现无法对x86进程进行检测

0x023 Yara

Yara是一个旨在(但不限于)帮助恶意软件研究人员识别和分类恶意软件样本工具

Elastic安全公司开源检测CobaltStrike的yara规则

Google GCTI开源检测CobaltStrike的yara规则

使用Elastic的yara规则检测beacon,可以看到命中了6条规则

使用-s参数打印出匹配的字符串

0x024 Hollows_Hunter

hollows_hunter用于扫描所有正在运行的进程,识别各种潜在的恶意植入物,如替换/植入的PE、shellcode、挂钩(hook)以及内存中的修补程序等

顺带提一嘴,Hollows_Hunter的作者Aleksandra Doniec在我看来是一位顶尖的安全研究员,开源了pe_to_shellcode、process_overwriting等优秀的作品,真正左右手互博

通过hollows_hunter可以很轻松的检测到一些异常的进程

0x03 反狩猎

针对以上问题,CobaltStrike官方在博客中提供了一些解决方法

0x031 Yara bypass

0x0311 字符串处理

可以看到Windows_Trojan_CobaltStrike_ee756db7匹配了很多字符串,我决定先看看这些字符串都是从哪里来的。

CobaltStrike在4.x之后,会把资源文件加密存放到cobaltstrike-client端的sleeve目录中,需要使用CrackSleeve对资源文件进行解密

CobaltStrike4.9.1的key如下,需要自行替换一下

private static byte[] OriginKey = {-1, 12, -6, 65, 7, -47, 91, 48, 17, 61, 29, 43, -99, -23, 21, 109};
private static byte[] CustomizeKey = {-1, 12, -6, 65, 7, -47, 91, 48, 17, 61, 29, 43, -99, -23, 21, 109};

对cobaltstrike-client及解密的Resource进行搜索,最后在default.profile发现了结果,而且与Windows_Trojan_CobaltStrike_ee756db7匹配的规则一致

把他复制出来,并删除stage里面内容作为Malleable-C2来使用,重新启动server,生成beacon上线

再次使用yara检测发现字符串匹配特征已经少了很多,但是还有一些存在

既然profile中的特征已经去除了,那么剩余的规则要么在原始beacon.dll中存在,要么就是生成的exe时出现的特征,先看看原始beacon.dll吧,使用yara单独对文件进行检测,可以明显的看到,确实是在原始beacon.dll中存在的特征

针对这种情况,CobaltStrike提供了可以从profile中使用strrep来替换指定的字符串,把其中的一个特征替换为空

transform-x64 {
    strrep "beacon.x64.dll" "";
}

再次生成beacon,运行发现ee756db7规则直接就消失了

???我看了一下Windows_Trojan_CobaltStrike_ee756db7的判定规则,发现该规则需要至少6个命中才会判定

虽然这种方法简单且有效,但是从实际考虑来说,我们不应该全部都这么做,因为无法确定其他安全公司使用的规则,如果修改了判断规则为3个你只修改其中一个,那肯定是不行的,并且有些格式化字符串也不应该直接修改,否则可能会给程序带来不可意料的结果,如Windows_Trojan_CobaltStrike_3dc22d14中还检测了一些格式化字符串

当然也不是没有解决方法。那就是sleepmask kit套件,后面会详细介绍

0x0312 MZ头/PE头处理

可以看到Windows_Trojan_CobaltStrike_1787eef5的特征为4D 5A,很明显该处检测的是MZ

可以从内存中看到,确实存在该特征

针对这种情况,CobaltStrike提供了可以在profile中配置 Stage.magic_mz_*/Stage.magic_pe_*对其进行修改

官方建议:需要注意的是,对于magic_mz_* 选项,提供的值必须是有效的(无)操作码,因为它们是作为shellcode存根的一部分执行的第一条指令。通常情况下,这将是pop regA,push regA的某种变体,因为后一条指令撤消了第一条指令,但请参阅此处以获得有关配置此选项的更多指导

修改mz头

set magic_mz_x86 "KC@H"; # ASM = dec ebx, inc ebx,inc eax, dec eax
set magic_mz_x64 "A[AS"; # ASM = pop r11, push r11

修改pe头

set magic_pe "AR"; # 随机的两个值

修改完成后在内存中的效果

使用yara进行检测的前后对比

然而,这种修改方式是有限的,因为我们在每种情况下只能修改几个字节,所以显然更健壮的YARA签名仍然会触发

同时官方还提供了一个Stage.stomppe用于轻微混淆内存中的 beacon dll,但是我在测试发现设置stomppe为true时,PE头中的仅仅在特征处增加了一个IMAGE_FILE_RELOCS_STRIPPED

未设置stomppe时

从微软的文档来看,我并不能明白这么做有什么好处,感觉很鸡肋,比较了解的师傅们回答我一下

0x0313 清理反射加载器

当Beacon被反射加载到内存中时,它会导致两个内存分配:原始Beacon DLL(实际上将执行shellcode存根和反射加载器函数)和虚拟Beacon DLL(正确加载到内存中并准备就绪)

在内存中的情况如下,RWX存储器区域对应于虚拟信标DLL,而RX区域则对应于原始信标DLL

同时原始信标DLL中也存在可疑字符串。这些都可以通过内存中的YARA扫描找到

前面的是原始beacon,后面的是配置strrep “beacon.x64.dll” “";去除字符串后的内存,还应该把ReflectiveLoader这个非常明显的特征给去除掉

扯远了,回到正题,针对这种情况,CobaltStrike提供了可以在profile中配置Stage.cleanup选项为true,对原始Beacon DLL进行清除,

仅保留虚拟Beacon DLL,一旦启动Beacon,就不再需要原始Beacon DLL了

set cleanup "true";

清理前后的内存对比

yara检测结果如下,很明显清除原始beacon dll后有些检测已经从2个变成一个了

0x0314 配置混淆

通过配置Stage.obfuscate为true,可以实现反射加载器复制Beacon,而不带它的DLL头,这就意味着在内存中无法再找到反射加载程序存根,而且这个选项还会混淆:

  • .text section
  • Section names
  • Import table
  • Dos/Rich Header (this is technically not masked but overwritten with random data)

大概的示例图如下:

这项设置可移除Beacon堆中的绝大部分字符串

set obfuscate "true";

后面是配置obfuscate为true的内存,可以看到直接去除掉了dll头部

yara检测设置obfuscate为true的前后对比

0x0315 Sleep_Mask

官方解释如下:

在启用Sleep_Mask之前,先了解一下userwx配置

set userwx "false";

反射加载时是否要把内存设置为可读可写可执行,默认为RWX,设置为false时内存设置为RX

然后配置启用sleep_mask

set sleep_mask "true";

正如官方所说,确实对字符串进行了加密,但是会多出一条新的规则,很明显sleep_mask默认的规则已经被检测了

在内存中也确实找到了这个规则

不是说sleep_mask会屏蔽自己吗?其实这项规则恰恰匹配的就是sleep_mask屏蔽的方法,如下图所示

使用arsenal-kit的sleepmask进行配置

在common_mask.c中自定义我们的算法

/* My a beacon section
 *   First call will mask
 *   Second call will unmask
 */
void my_mask_section(SLEEPMASKP * parms, DWORD a, DWORD b) {
   char key[] = "cf81d743beef8422";
   size_t key_lenght = sizeof(key) - 1;
   while (a < b) {
      *(parms->beacon_ptr + a) ^= key[a % key_lenght];
      a++;
   }
}

最后重新构建并重新加载.cna脚本,以使更改生效

yara检测使用自定义算法的beacon,最后只剩一条特征了

在内存中默认算法和自定义加密算法的对比

0x0316 加载器特征去除

0x03161 shellcode loader

最后的这个特征,其实是生成exe时附带的。如果使用shellcode loader进行上线这一个部分就不需要更改了

不过使用shellcode loader要注意需要对存放shellcode的内存进行加密或者清理,非常简单的代码,主要是为了演示

#include<iostream>
#include<windows.h>
#include<fstream>

using namespace std;

int main()
{
    // shellcode raw 
	char filePath[] = "./payload_x64.bin";
	ifstream file(filePath, ios::binary | ios::ate);
	if (!file) {
		return -1;
	}
	int fileSize = file.tellg();
	file.seekg(0, ios::beg);

	char* buffer = new char[fileSize];
	if (!file.read(buffer, fileSize))
	{
		return -2;
	}
	void* exec = VirtualAlloc(0, fileSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	memcpy(exec, buffer, fileSize);

    // 对buffer进行加密
	string key = "cf81d743beef8422";
	for (int i = 0; i < fileSize; i++)
	{
		buffer[i] = buffer[i] ^ key[i % key.length()];
	}

	((void(*)())exec)();

	return 0;
}

效果如下

0x03161 源码修改

当然如果你追求完美,可以接着往下看,不过首先说明,通过套件的方式进行修改的只能在生成exe文件的时候有效,shellcode还是需要使用完成在内存进行加密

首先先定位一下特征,我直接使用ida对该字节码进行搜索

伪代码看一下,看起来是//./pipe/MSSE-随机整数-server的通道生成

在CobaltStrike的博客中有提到这个问题,指明了可以通过Artifact Kit中的src-common/bypass-pipe.c进行修改

当然,如果你不想使用多余的套件,可以自行反编译修改并打包原始beacon.dll进行

我这边就演示在bypass-pipe.c中进行修改,注释部分的是Artifact Kit中默认的,该方法也已经被yara标记了,我做的只是简单的字符串隐藏

因为使用了arsenal-kit中的artifact-kit和sleepmask-kit,所以直接修改arsenal-kit配置文件生成一个套件即可

修改的位置如下:

  • /arsenal-kit/kits/artifact/build.sh:49-51行,给它注释掉就不会报错了
  • /arsenal-kit/arsenal_kit.config:16行,设置include_sleepmask_kit=“true”,因为还启用了sleepmask-kit

接下来是Artifact kit options和Sleepmask kit options,根据实际情况修改即可

#### Artifact kit options
artifactkit_technique="pipe"
artifactkit_allocator="HeapAlloc"
artifactkit_stage_size=310272
artifactkit_include_resource="false"
artifactkit_stack_spoof="false"
artifactkit_syscalls_method="indirect"

#### Sleepmask kit options
sleepmask_version="49"
sleepmask_sleep_method="WaitForSingleObject"
sleepmask_mask_text_section="true"
sleepmask_syscalls_method="indirect"

运行/arsenal-kit/build_arsenal_kit.sh生成即可,生成后的路径为/arsenal-kit/dist/

加载该套件,重新生成beacon,运行上线,使用yara对进程进行检测,可以看到和shellcode loader上线一样是检测不到的

以上是x64的修改,x86也同样适用,不过x86需要额外修改一下2个位置

  • /arsenal-kit/kits/artifact/src-common/bypass-pipe.c中的DWORD server_thread(LPVOID whatever) 方法
  • 打乱一下它的结构就行

  • /arsenal-kit/kits/artifact/src-common/patch.c
  • 也是打乱一下结构

0x04 效果测试

其实到了这一步已经能解决狩猎中的所有检测了

yara静态检测

yara内存检测

BeaconEye/EvilEye

Hunt-Sleeping-Beacons

Hollows_Hunter

配合shellcode loader对抗大部分杀软了

卡巴内存扫描

火绒

0x05 结语

到此为止,配合一下自定义的Malleable-C2足以应付大部分红队场景,如果还想进一步,建议配合unhook、堆栈欺骗等技术

嘿嘿,如果你以为这就结束了,那就错了,如果说我针对Artifact Kit套件进行yara打标呢?以下是另一个,上面是Elastic的检测,下面是自己针对Artifact Kit套件写的规则

from https://blog.aruiredteam.com/posts/

相关推荐

设置文件的默认打开方式(如何设定文件的默认打开方式)

在操作系统中,设置文件的默认打开方式可以让特定类型的文件始终使用你选择的程序打开。以下是Windows和macOS系统中设置默认打开方式的详细步骤:Windows系统方法1:通过文件属性设置右键点...

电脑怎么设置默认浏览器(电脑怎么设置默认浏览器?)

在电脑上设置默认浏览器的步骤因操作系统不同而有所差异。以下是Windows和Mac系统的设置方法:Windows系统方法1:通过系统设置打开“设置”:...

Java接口默认方法:灵活与约束并存

Java接口默认方法:灵活与约束并存在Java编程的世界里,接口作为定义行为规范的重要工具,一直扮演着举足轻重的角色。然而,在Java8引入了默认方法之后,接口的设计和使用方式发生了微妙的变化。今天...

Java8新特性之默认方法:为接口注入灵魂

Java8新特性之默认方法:为接口注入灵魂Java8发布时,它带来的最大创新之一就是接口的默认方法。这就像给一个传统的木偶注入了生命,让它不仅能动还能说话了。今天我们就来聊聊这个让Java开发者欢呼雀...

CentOS系统在不重启的情况下为虚拟机添加新硬盘

一、概述用过虚拟机的都知道,如果在系统运行的时候去给虚拟机添加一块新设备,比如说硬盘,系统是读取不到这个新硬盘的,因为系统在启动的时候会去检测硬件设备。但是我们也可能会遇到这样的情况,比如正在运行比较...

[常用工具] 基于psutil和GPUtil获取系统状态信息

本文主要介绍在Python3中利用psutil库获取系统状态,利用GPUtil获取gpu状态。psutil(processandsystemutilities)(进程和系统实用程序)是一个跨平...

Docker容器内执行宿主机指令(docker执行宿主机shell)

一背景最近项目有个需求,需要程序配置服务器IP并且可以重启服务器。如果程序直接部署在服务器,相信大家都会操作。但是程序是用docker运行的,在docker中执行指令就很麻烦了。...

容器网络调试怎么办?一条命令就搞定!

nsenter命令是一个可以在指定进程的命令空间下运行指定程序的命令。它位于util-linux包中。用途...

linux自动化巡检脚本集(linux服务器巡检内容以及标准)

以下是一个自动化巡检脚本集的制作方案,包含常见系统检查项和可扩展框架,使用Python和Shell脚本实现:...

麒麟KOS|统信UOS系统添加新数据盘
麒麟KOS|统信UOS系统添加新数据盘

原文链接:麒麟KOS|统信UOS系统添加新数据盘...

2025-03-26 16:57 suiw9

Linux CentOS 7 根目录扩容(linux扩展根目录磁盘空间)

一、现状描述现有一台CentOS7的虚拟机,硬盘容量为30GB通过lsblk命令查看分区容量,当前根目录容量为27.5GB左右二、扩容需求...

从Linux底层分析Docker原理(linux docker-compose)

写在前面如果你觉得本人对你有帮助,请你记得评论,点赞,关注;如果你觉得文章还不错请记得收藏,点赞。如果你觉得文章非常的好可以私信我,我会在第一时间回复你。...

CentOS新增硬盘的使用步骤(centos扩展硬盘)

一、查看机器所挂硬盘个数及分区情况,新增硬盘为/dev/sdb...

Python之psutil库简介(python3 pil库)

psutil(pythonsystemandprocessutilities)是一个跨平台的第三方库,能够轻松实现获取系统运行的进程和系统利用率(包扩CPU、内存、磁盘、网络等)信息。它主...

运维,你还不会查看Linux系统cpu信息?

CPU也称为微处理器或简称为处理器。就像大脑如何控制人体一样,CPU控制着计算机的所有部分。因此CPU被认为是计算机的大脑。那我们怎么在Linux系统中查看如IntelCorei3、i5、AM...

取消回复欢迎 发表评论: