从 Cobalt Strike 信标中挖掘数据

自从我们在三年多前发布了关于在野外识别 Cobalt Strike 团队服务器的信息以来,我们已经从超过 24,000 个活跃的团队服务器中收集了超过 128,000 个信标。今天,RIFT 正在公开这个广泛的信标数据集,并结合我们用于研究和解析 Cobalt Strike 相关数据的 Python 库的开源版本dissect.cobaltstrike
发布的数据集包含从 2018 年到 2022 年的历史信标元数据。本博客将重点介绍一些有趣的发现,您可以从这个广泛的数据集中提取和查询。我们鼓励其他研究人员也探索数据集并与社区分享令人兴奋的结果。

Cobalt Strike Beacon 数据集

该数据集beacons.jsonl.gz是一个 GZIP 压缩文件,包含 128,340 行信标元数据作为 JSON 行。您可以从以下存储库下载它,并确保还查看随附的 Jupyter 笔记本:

该数据集涵盖了从 2018 年 7 月到 2022 年 2 月近四年的历史 Cobalt Strike 信标元数据。不幸的是,由于存档问题,我们在 2019 年丢失了五个月的数据。此外,该数据集主要关注从 HTTP 端口 80、443 和 DNS 上的活动 Team Server 收集的 x86 信标;因此,它不包含来自其他来源的任何信标,例如 VirusTotal。
由于大小,信标有效负载本身不在数据集中。相反,会存储不同的信标配置设置,包括其他元数据,例如:

  • 收集信标的日期以及来自哪个 IP 地址和端口
  • GeoIP + ASN 元数据
  • DER 格式的 TLS 证书
  • PE 信息(时间戳、magic_mz、magic_pe、stage_prepend、stage_append)
  • 如果负载是 XOR 编码的,以及哪个 XOR 密钥用于配置混淆
  • 原始信标配置字节;如果您想手动解析信标配置,则很方便。(例如使用dissect.cobaltstrike或其他选择的解析器)

虽然有一些简单的方法可以从信标有效负载中识别破解/盗版的 Cobalt Strike Team 服务器,但很难分辨出那些不重要的方法。因此,数据集是未经过滤的、完全公开的,并且包含我们收集的所有信标。
当然,正确隐藏或禁用有效负载分段的 Cobalt Strike 团队服务器不包括在内。这意味着拥有良好 OPSEC 的红队(以及可悲的威胁参与者)无需担心出现在此数据集中。
【🤯重要🤯】【翻译收集】Fox-IT- 从 Cobalt Strike 信标中挖掘数据 - 图1

信标,以及在哪里可以找到它们

checksum8Cobalt Strike 信标是通过首先识别 Internet 上的团队服务器,然后使用HTTP 请求下载信标来获取的。这种方法类似于 Cobalt Strike 背后的公司在 2019 年 进行自己的Cobalt Strike 团队服务器人口研究的方式。
虽然我们用于识别的异常空间指纹已经修复,但我们找到了其他可靠的方法来识别团队服务器。此外,我们确信 Cobalt Strike 的原作者故意在其中留下了一些指标来帮助蓝队。为此,我们心存感激,并希望这在未来不会改变。
通过我们的蜜罐,我们可以看出 RIFT 在挖掘信标方面并不孤单。现在每个人都在这样做。关于如何找到 Cobalt Strike 的博客文章和示例脚本的增加可能归因于这一点,当然,除了 Cobalt Strike 本身的日益普及。
Cobalt Strike 增强扫描的发展令人着迷,包括用于识别团队服务器和检索信标的不同技术和霰弹枪方法。有些甚至跳过了识别部分,直接去请求信标!正如您可以想象的那样,这可能很嘈杂,对于某些威胁参与者来说肯定不会被忽视。
如果您运行面向公众的 Web 服务器,您可以通过检查 HTTP 访问日志中常见的checksum8类似请求来轻松验证这种增加的扫描,例如,使用以下grep命令:

  1. $ zgrep -hE "GET /[a-zA-Z0-9]{4} HTTP" /var/log/nginx/*.gz
  2. 172.x.x.x - - [23/Feb/2021:18:xx:08 +0100] "GET /0bef HTTP/1.0” 404 162 "-" "-"
  3. 172.x.x.x - - [24/Feb/2021:09:xx:40 +0100] "GET /0bef HTTP/1.0” 404 162 "-" "-"
  4. 139.x.x.x - - [25/Feb/2021:05:xx:39 +0100] "GET /bag2 HTTP/1.1” 404 193 "-" "-"
  5. 134.x.x.x - - [25/Feb/2021:15:xx:12 +0100] "GET /ab2g HTTP/1.1” 400 166 "-" "-"
  6. 134.x.x.x - - [25/Feb/2021:15:xx:22 +0100] "GET /ab2h HTTP/1.1” 400 166 "-" "-"

上面显示的请求是checksum8请求(针对 x86 和 x64 信标),在 2021 年 2 月访问托管真实网站的普通网络服务器。
您也可以使用我们的checksum8-accesslogs.py脚本,它在一个脚本中完成所有这些事情,并且通过验证checksum8值更准确。它还可以输出统计信息。这是一个输出 x86 和 x64 信标 HTTP 请求命中我们的一个蜜罐并生成统计信息的示例:
image.png
checksum8-accesslog.py 脚本在访问日志中发现可能的 Beacon stager 请求
在输出中,您还可以看到正在使用的不同信标扫描技术,我们将把它作为练习留给读者来了解。
通过绘制统计数据,我们可以看到我们的一个蜜罐上的信标扫描明显增加:

Cobalt Strike Beacons

image.png
RIFT增加了对信标的扫描
这张图显示了每个月有效的checksum8分段请求到达一个集合的RIFT蜜罐的数量
因此,如果您想知道为什么人们在您的 Web 服务器上请求这些奇怪的四字符 URL(或其他看起来很奇怪的 URL),请检查checksum8请求的值,您可能会得到答案。
我们尽量隐蔽一点,不会透露我们的指纹识别技术,因为我们也知道威胁参与者很警惕,从长远来看,这将使每个人都更难处理威胁情报。

Cobalt Strike 版本随时间的使用情况

因为我们拥有多年的信标元数据,我们可以很好地描绘 Internet 上活跃的 Cobalt Strike 服务器以及它们当时使用的版本。
为了提取 Cobalt Strike 版本数据,我们使用了以下两种方法:

  • 使用信标设置常量
    • 当引入新的 Cobalt Strike 信标配置设置时,设置常量会增加然后分配。可以根据提取的信标配置中的最高可用常量来推断版本。
  • 使用 PE 导出时间戳

我们的 Python 库dissect.cobaltstrike支持这两种推断版本号的方法,并在可用时支持 PE 导出时间戳。
为方便起见,数据集已包含该beacon_version字段,并且基于 PE 导出时间戳。使用此字段,我们可以生成以下图表,显示 Internet 上使用的不同 Cobalt Strike 版本随时间的变化:

Cobalt Strike version usage over time

image.png
Internet 上活跃的 Cobalt Strike 服务器的版本采用情况
我们可以看到,在 2021 年 4 月,在线 Cobalt Strike 服务器和独特的信标出现了相当显着的高峰,但我们不确定是什么原因造成的,除了当月修改过的(可能是恶意的)信标增加了 3%。
以下百分比图表更清晰地显示了不同版本之间的采用和流行度。

Cobalt Strike versions usage percentage wise

image.png
百分比图表的版本采用的Cobalt Strike服务器在互联网上
这里聚合了<= 3.12的版本
我们可以看到,Cobalt Strike 4.0(2019 年 12 月发布)在 2020 年 1 月至 2021 年 1 月期间仍然很受欢迎。

信标水印统计

自 Cobalt Strike 3.10(2017 年 12 月发布)以来,信标包含一个名为SETTING_WATERMARK. 这个水印值在每个 Cobalt Strike 安装中应该是唯一的,因为许可证服务器会发布它。
但是,破解/盗版版本通常会将其修补为固定值,从而可以轻松识别哪些信标更有可能是恶意的(即不是渗透测试人员)。这种可能性与我们迄今为止的事件响应活动相一致,其中与妥协相关的信标使用了已知的不良水印。
请注意,恶意行为者很难申请试用或购买 Cobalt Strike 的合法副本,因为每个用户都经过审查和筛选。由于这些措施,在暗网上购买 Cobalt Strike 副本的要价很高。例如,Conti 投资 60.000 美元购买了一份 Cobalt Strike 的有效副本

Top 50 Cobalt Strike Beacon Watermarks

在独特的信标数据集上看到的前50个水印。 高级别水印更有可能被“修改”的团队服务器:
image.png
如果您在这前 50 个中发现带有水印的信标,那么它很可能是恶意的!

定制信标

在解析收集到的信标时,我们发现一些信标被修改了,例如,使用自定义 shellcode 存根、非默认 XOR 键或重新分配的信标设置。
因此,具有大量自定义的信标无法正确转储并且不包含在数据集中。
信标有效负载中的配置块通常使用单字节 XOR 密钥进行混淆。根据 Cobalt Strike 版本,默认键是 0x2e 或 0x69。
使用非默认 XOR 键需要用户修改信标和/或团队服务器,因为默认情况下不可配置。以下是在唯一信标数据集上看到的 XOR 键的概述:

Customized XOR keys

使用非默认的异或键意味着Beacon被修改了,因为这在Cobalt Strike中是不可配置的。 这个表显示了在唯一的信标数据集上看到某个XOR键的次数。 最上面的两个键是Cobalt Strike默认使用的键:
image.png
虽然使用自定义 XOR 密钥会使您成为异常值,但它确实可以保护您免受一些现有的 Cobalt Strike 配置转储程序的影响。我们的 Python 库dissect.cobaltstrike支持在默认 XOR 键不起作用时尝试所有 XOR 键。例如,您可以将命令行标志—all-xor-keys传递给beacon-dump命令。

可移植的可执行工件

虽然大多数现有的 Cobalt Strike 转储程序都专注于信标设置,但 Malleable C2 配置文件中的一些设置不会最终出现在有效载荷的嵌入式信标配置中。例如,Malleable C2 配置文件中的一些可移植可执行文件 (PE) 设置直接应用于信标负载。我们的 Python 库dissect.cobaltstrike支持提取此信息,我们的数据集包括以下提取的 PE 标头元数据:

  • magic_mz:MZ 标头
  • magic_pe:PE 头
  • pe_compile_stamp:PE 编译戳
  • pe_export_stamp:导出部分的时间戳
  • stage_prepend:(shellcode)字节添加到信标有效负载的开头
  • stage_append:附加到信标有效负载末尾的字节

我们创建了所有 ASCII字节的最常见stage_prepend字节的概述。这些字节位于 MZ 标头前面,并且必须是有效的汇编代码,但在作为 shellcode 执行时会导致无操作。有些很有创意:

Top ASCII stage_prepend shellcode bytes

最常用的”ASCII” stage_preend shellcode字节。 通常这是“\x90\x90”,等等:
image.png
如果我们反汇编示例 stage_prepend shellcode JFIFJFIF,我们可以看到它增加了 ESI 并减少了 EDX 寄存器并因此对其进行了修改;所以它不完全是一个无操作的 shellcode,但它很可能也不影响暂存过程:

  1. $ echo -n JFIFJFIF | ndisasm -b 32 /dev/stdin
  2. 00000000 4A dec edx
  3. 00000001 46 inc esi
  4. 00000002 49 dec ecx
  5. 00000003 46 inc esi
  6. 00000004 4A dec edx
  7. 00000005 46 inc esi
  8. 00000006 49 dec ecx
  9. 00000007 46 inc esi

您可以查看我们的Jupyter 笔记本,了解其余 PE 工件的概述,例如magic_mz和magic_pe.

使用空格加水印的 releasenotes.txt

Cobalt Strike 的作者一定很喜欢空格,在 HTTP 服务器标头中的错误空格之后,现在还有一个称为(重新调整用途的)信标设置SETTING_SPAWNTO,现在填充了文件的 MD5 哈希值releasenotes.txt(或者意外地填充了同一文件中的另一个文件)目录,如果它匹配相同的checksum8值 152 和文件名长度!)。
当releasenotes.txt您激活或更新 Cobalt Strike 服务器时,它会自动从许可证服务器下载。令我们惊讶的是,我们发现这个文件很可能是使用空白字符加水印的,从而使这个文件和 MD5 散列在每次安装时都是唯一的。许可证服务器可能会跟踪所有这些唯一生成的文件,以帮助打击盗版和 Cobalt Strike 的泄漏。
虽然这非常聪明,但我们发现在一些盗版信标中,该字段全为零,或者不可用。这意味着他们知道这个文件,并决定不在盗版版本中发布它,否则字段值已被修补。尽管如此,当可用时,该字段仍可用于搜索或关联信标。
image.png
请注意两个 releasenotes.txt 文件之间行尾的细微空格变化。

使用 dissect.cobaltstrike 分析 Beacon 有效载荷

我们也很自豪地开源了用于剖析 Cobalt Strike 的 Python 库,该库名为dissect.cobaltstrike. 该库在 PyPI 上可用,需要 Python 3.6 或更高版本。你可以使用 pip 来安装它:

  1. $ pip install dissect.cobaltstrike

项目的 GitHub 存储库:https ://github.com/fox-it/dissect.cobaltstrike
为了您的方便,它目前安装了三个命令行工具:

  • beacon-dump:用于从信标负载转储配置(也适用于内存转储)
  • beacon-xordecode:用于解码异编码有效载荷的独立工具
  • c2profile-dump:使用它来读取和解析 Malleable C2 配置文件。

“beacon-dump”的一个简洁的特性是将beacon配置转储回来,因为它是可延展性C2配置文件兼容的等效文件:
image.png
将信标设置转储为可扩展 C2 配置文件
虽然这些命令行工具提供了使用 Beacon 有效负载的大部分样板,但您也可以将库导入脚本或笔记本中以用于更高级的用例。有关一些示例,请参阅我们的笔记本文档

结束的想法

信标数据集已被证明对我们非常有用,尤其是数据集的历史方面在事件响应参与期间很有洞察力。我们每天使用数据集,范围从 C2 基础设施映射、参与者跟踪、威胁追踪、高质量指标、检测工程等等。
我们希望这个数据集和 Python 库能像对我们一样对社区有所帮助,并渴望看到人们使用这些数据和工具会想出或发现什么样的令人兴奋的事情。我们在本博客中展示的内容只是您可以从信标数据中发现的冰山一角。
给读者的一些想法:

  • 使用 DBSCAN 等聚类算法对信标和 C2 配置文件特征进行聚类。
  • 改进恶意信标的分类。您可以在我们的笔记本中找到当前的分类方法。
  • 使用 GeoIP ASN 数据确定最恶意信标的托管位置。
  • 分析 x509 证书数据,例如是否自签名。
  • 确定信标是否使用域前端以及哪个 CDN。

这篇博文中显示的所有统计信息也可以在我们随附的 Jupyter 笔记本中找到,包括本博文中未显示的更多统计信息和概述。
我们还要感谢 Rapid7 提供的开放数据集,如果没有这些数据,信标数据集的完整性就会大大降低!
为方便起见,最终链接: