如何发现信标(二) - 嘶吼 RoarTalk – 回归最本质的信息安全,互联网安全新媒体,4hou.com

如何发现信标(二)

xiaohui 技术 2022-08-13 11:40:00
收藏

导语:我们在本文中介绍了对 C2 框架执行威胁追踪的通用方法以及针对 Cobalt Strike 的实际示例。

如何发现信标(一)

我们在上一篇文章介绍了对 C2 框架执行威胁追踪的通用方法以及针对 Cobalt Strike 的实际示例。接下来,我们将分析由 Dark Vortex 开发的命令和控制框架 Brute Ratel。其 C2如下所示 :

1.png

在过去的几个月里,该框架受到了密切关注,据称最近被 APT29 和勒索软件组织 BlackCat 滥用。因此,了解如何在基础设施中普遍地检测这个新兴的C2对于防御者来说是有用的。

最初,所有分析都是在Brute Ratel v1.0.7上执行的,但是,我们进行了粗略的更新,讨论了与 v1.1 相关的发现,该发现是在我们最初的 x33fcon 演示后不久发布的。 Brute Ratel 应该注意的一件事是,badger 的扩展性有限,主要从 c2 通道的角度来看,除了v1.1 ,它增加了休眠混淆技术的扩展性。因此,它可以为工具创建非常具体的检测。

Brute Ratel 的加载器

Brute Ratel 的badger有多种形式,包括 exe、DLL 和 shellcode。当 badger 被注入时,它的反射加载器将立即加载 badger 所需的所有依赖项。由于 badger 捆绑了大量的后利用功能,这导致在初始化时加载大量 DLL:

2.png

如上所示,突出显示的 DLL 是注入 badger 时加载的所有 DLL。此列表包括 winhttp.dll 和 wininet.dll 的加载,它们不一定是恶意的,而是输出信标的传统加载。然而,还有一些不太常见的dll被加载,比如dbghelp.dll、credui.dll samcli.dll和logoncli.dll等。

这种行为允许我们为图像加载创建一个签名,并产生一个高信号指示器,可以通过图像加载检测来寻找。

例如,使用弹性查询语言,我们可以搜索credui.dll、dbghelp.dll和winhttp.dll在一个进程中相互间隔60秒内发生的加载事件序列:

3.png

使用 EQL 工具或 Elastic 的云,我们可以搜索我们的事件数据,例如从 sysmon 日志中提取的以下内容。请注意,我们明确排除了 badger 可执行文件本身,因此我们只能识别注入的 badger:

4.png

这将导致以下显示检测到被注入 notepad.exe 的badger:

5.png

这个查询特别强大,因为它允许我们在网络中追溯寻找 Brute Ratel badger的指标,而无需直接在终端上运行代码。

内存中的Brute Ratel

由于大多数信标仍然驻留在内存中,因此了解留下的足迹以寻找它们非常重要。查看 1.0 版本的 Brute Ratel 文档,它详细介绍了它自己的混淆和休眠实现:

6.png

这个查询特别强大,因为它允许我们在网络中追溯寻找 Brute Ratel badger的指标,而无需直接在终端上运行代码。

由于大多数信标仍然驻留在内存中,因此为了寻找它们,了解留下的足迹是很重要的。回顾一下Brute Ratel 1.0版本的文档,它详细介绍了它自己的混淆和休眠实现:

7.png

一旦badger进入休眠状态,我们就可以使用 Process Hacker 从进程中恢复字符串。有趣的是,当badger休眠时,我们可以看到如下字符串:

8.png

鉴于前面提到的在 Brute Ratel 博客上描述的所谓的休眠和混淆策略,这最初是相当令人惊讶的。

深入挖掘后,我们可以发现一些有趣的设计决策,其中显示在操作员 UI 中的许多字符串都是从badger本身填充的。例如,我们可以在badger休眠时的内存中看到以下内容:

9.png

然后这些字符串返回到 UI,如下所示:

10.png

深入研究badger,很快就发现只有 .text 部分在休眠时被混淆,使得badger容易受到针对字符串和数据的各种签名的影响。

为了说明这一点,反转 badger,我们可以将加载程序的入口点视为“bruteloader”:

11.png

在badger休眠时在内存中搜索这个字符串,我们可以在记事本进程中快速找到它:

12.png

这些字符串为内存扫描的 Yara 规则提供了一个很好的基础。例如,以下规则将在进程的内存中搜索 bruteloader 或 bhttp_x64.dll 字符串:

13.png

我们可以在badger休眠时针对我们的记事本进程测试这些以证明其有效性:

14.png

字符串不太可能存在于其他进程中,使用简单的一行代码就可以快速找到测试系统中所有被注入的badger:

15.png

使用Yara 规则,我们可以快速找到其他样本,例如:

16.png

页面权限

通过对Brute Ratel混淆和睡眠策略的分析,可以观察到badger在休眠期间对badger的页面权限进行调整,以试图逃避badger休眠时延长的可执行权限。

下面,我们可以看到 badger 在 sleep 0 上运行,badger 的页面权限在未映射的页面上为 PAGE_EXECUTE_READ,这对于执行任务是必要的。

17.png

让badger进入休眠状态,我们可以看到混淆和休眠策略混淆了 .text 部分并将badger的页面权限重置为 PAGE_READWRITE:

18.png

有趣的是,我们注意到在执行 SMB 数据透视时不会复制此行为,即当两个badger被关联时。在这里,我们可以看到我们的两个badger相互关联,并且都处于 60 秒的休眠状态:

19.png

对两个badger 关联时的页面权限分析表明,无论休眠时间如何,两者都保持 PAGE_EXECUTE_READ:

20.png

结论是混淆和休眠策略仅适用于 .text 部分。

出于对模糊处理和睡眠功能如何工作的好奇,我们开始对其进行逆向工程。通过 windbg 中的 sleep 例程,我们可以初步了解正在发生的事情,badger正在使用 WaitForSingleObjectEx 在一系列异步过程调用 (APC) 期间延迟执行,并利用间接系统调用来执行 NtTestAlert 并在线程上强制发出警报:

21.png

深入了解 IDA,我们可以更好地了解正在发生的事情。首先,它创建一个新线程,其起始地址被欺骗到 TpReleaseCleanupGroupMembers+550 的固定位置:

22.png

然后为 NtWaitForSingleObject、NtProtectVirtualMemory、SystemFunction032、NtGetContextThread 和 SetThreadContext 的多个函数调用创建一系列上下文结构:

23.png

接下来,许多APC 排队等待 NtContinue,目的是使用它来代理对上述上下文结构的调用,这种技术是ROP的基本形式:

24.png

在对睡眠技术进行逆向工程后,我们很快意识到它与@ilove2pwn_的Foliage项目非常相似,只是硬编码的线程起始地址不同。

尽管对badger进行了大量的调试和逆向工程,但我们无法揭示 v1.0 博客文章中引用的“Windows 事件创建、等待对象和计时器”技术的任何实证,事实上,这些技术所需的 API 似乎并没有通过 badger 的哈希导入来输入。

Brute Ratel 线程

为了分析 Brute Ratel 线程在内存中的外观,我们将 badger 注入到记事本的新副本中。随即,我们可以看到休眠的badger使用的线程中有一些可疑的指标。

首先,我们注意到有一个看起来可疑的线程,其起始地址为 0x0,并且在调用堆栈中有一个调用 WaitForSingleObjectEx 的单独的帧:

25.png

根据对线程调用堆栈的分析,我们可以推测该线程用于 HTTP 通信,而badger现在正在休眠:

26.png

根据我们从对混淆和休眠策略进行逆向工程获得的信息,我们注意到新线程是使用硬编码的欺骗起始地址 ntdll!TpReleaseCleanupGroupMembers+0x550 创建的:

27.png

我们无法找到任何作为起始地址自然发生的实例,因此导致了一个用于寻找 Brute Ratel 线程的微不足道的指标。实际上,我们注入的记事本进程如下所示:

28.png

线程的调用堆栈也略有不规则,因为它不仅包含延迟执行的调用,而且第一帧指向 ntdll.dll!NtTerminateJobObject+0x1f。深入了解为什么使用 NtNerminateJobObject 会突出显示这只是 NtTestAlert 的 ROP 小工具,用于在线程上执行挂起的 APC:

29.png

内存挂钩

在第一篇文章中,我们详细介绍了两种基于内存挂钩检测内存信标的潜在方法,通过查找已知补丁的签名(例如ret to ntdll.dll!EtwEventWrite)并通过检测写入操作的副本。

将这些概念应用到Brute Ratel中,我们注意到,在操作员使用其开发后功能之前,badger不会应用任何内存挂钩。一个例子是Sharpinline 命令,它在当前进程中运行一个.NET 程序集:

30.png

一旦组装完成并且信标重新进入休眠状态,我们可以通过附加调试器并反汇编 ntdll.dll!EtwEventWrite 和 amsi.dll!AmsiScanBuffer 的值来更好地了解发生了什么:

31.png

如上所示,这些是禁用 .NET ETW 数据和禁止 AMSI 的简单且持久的补丁。由于补丁是持久的,我们可以通过上述任何一种技术来检测它们,因为我们不仅会因为 EtwEventWrite 的第一条指令是 ret 而收到高信号检测,而且还会因为EtwEventWrite所在的页面由于共享位的清除而被修改。

使用BeaconHunter,我们可以通过解析修改页面上的导出信息,快速检测出这些挂钩,为恶意篡改提供了强有力的指示:

32.png

Brute Ratel C2 服务器

远离终端,作为防御者,我们也有兴趣检测命令和控制基础设施,因为这可能有助于为我们提供足够的智能来检测基于网络检测的信标。

Brute Ratel的C2服务器是用golang开发的,默认情况下只允许操作员修改C2的默认登录页面。为了识别C2服务器,我们发现在向任何URI发送包含base64的POST请求时,都可能生成未处理的异常。例如,考虑下面base64 POST数据与明文数据的比较:

33.png

这很可能是因为 base64 解码 POST 数据的预期输入应符合 C2 通信格式。一个简单的 Nuclei 规则可能会帮助我们扫描这种基础设施:

34.png

除了与 C2 的直接交互之外,还可以检测 C2 基础设施,其中操作员没有根据 HTML 的哈希 (http.html_hash=-1957161625) 手动重新定义默认登录页面。

使用一个简单的Shodan查询,我们可以快速找到暴露在互联网上的实时基础设施:

35.png

虽然只确定了大约 40 个组织服务器,但根据地理分布,我们可以更好地了解这些服务器的位置:

36.png

其中一些技术很可能已经为人所知,因为根据针对我们测试基础设施的报告,防御者正在积极寻找这些 C2 服务器:

37.png

蛮横的Ratel 配置

对 Badger 的分析表明,Brute Ratel 在内存中维护了一个加密的配置结构,其中包括 C2 终端的详细信息。能够从工件或正在运行的进程中提取这一点对防御者很有帮助。我们的分析表明,此配置保存在 base64 和 RC4 加密的 blob 中,使用badger的工件中的固定密钥“bYXJm/3#M?:XyMBF”,而配置以明文形式存储在内存中供休眠的badger使用。

我们开发了以下配置提取器,可用于 BRC4 v1.0.x 的磁盘上工件或使用 Brute Ratel 1.0.x 和 1.1.x 注入休眠的badger。

在工件或正在运行的进程(即使在休眠时)上运行提取器工具,将提取进程或工件的 Brute Ratel 配置状态:

39.png

更新 v1.1 分析

就在我们在x33fcon上讨论这个主题之后不久,Brute Ratel宣布了该软件的新版本。

混淆和休眠技术分析

关于 v1.1 版本让我们印象深刻的一件事是开发者发现了新的休眠和混淆技术的声明。对 Brute Ratel v1.1 中使用的休眠技术中的混淆进行逆向工程显示,现在可以使用三种休眠策略。第一个,正如我们之前记录的那样,是与@ilove2pwn_ 的 Foliage 极其相似的实现。

第二个实现,逆向工程显示与@c5pider 的 Ekko 代码几乎相同。

将此与在 Brute Ratel 中实现的技术进行比较:

41.png

如你所见,代码几乎相同。

Brute Ratel 使用的第三种技术,它是计时器的一种变体,并且没有公开记录,这种技术对计时器使用了细微的变化,而是通过 RtlRegisterWait 代理计时器:

42.png

虽然这种技术没有公开记录,但它已经在 Nighthawk 中使用了一段时间,巧合的是,许多常量使用了相同的值。

到目前为止,我们只讨论了 Brute Ratel 的 x64 实现中可用的休眠技术。对 x86 实现的分析表明,混淆和休眠策略被固定到上述基于 APC Foliage 的实现(注意断点从未命中):

43.png

迄今为止,还没有使用计时器的混淆和休眠策略的公共或开源 x86 实现,这限制了无需自定义开发即可轻松集成此类代码的可用机会。

内存检测

v1.1 版本中的一项更新意味着 .rdata 部分现在也被混淆了,以隐藏诸如“[+] AMSI Patched”之类的字符串,这些字符串暴露在休眠的badger的内存中。然而,分析表明,休眠的badger的内存中仍然存在许多暴露的字符串。因此,这意味着有很多机会在终端上提取 Brute Ratel 进程,即使在badger休眠时也是如此。

考虑到这一点,我们能够构建一个简单但有效的 Yara 规则来从内存中提取休眠的 Brute Ratel 进程:

47.png

执行 Yara 规则,我们可以发现休眠的badger:

48.png

v1.0中记录的对利用后行为(如写操作上的可疑副本)的检测仍然有效,并且仍然为BRC4利用后的检测提供了一种有效的手段。

线程堆栈欺骗

在 Brute Ratel 的 v1.0 版本中,我们注意到线程的起始地址被硬编码为 ntdll!TpReleaseCleanupGroupMembers+0x550。 1.1 版宣称提供“全线程堆栈伪装”。对 Brute Ratel 的堆栈欺骗的分析揭示了重写线程调用堆栈的简单实现。这个过程发生在badger休眠之前,使用前面提到的计时器技术。为了使线程看起来更合法,创建了一个新的线程堆栈,其中前两帧的地址是硬编码的。硬编码的地址分别位于 RtlUserThreadStart 和 BaseThreadInitThunk 的偏移量 0xa 和 0x12 处:

49.png

我们能够使用这些硬编码的起始地址识别任何其他线程,因此识别系统上的任何 Brute Ratel 线程变得微不足道。为了检测这些线程,我们相应地更新了 BeaconHunter,以识别前两帧为RtlUserThreadStart+0xa和BaseThreadInitThunk+0x12的线程:

50.png

更新 rDLL 提取

在我们对x33fcon进行分析后不久,Brute Ratel宣布对隐藏反射DLL的方法进行更新。对这些工件的分析表明,这是通过使用RC4用随机密钥加密反射DLL实现的,然后stomp  PE 标头。 8字节的RC4密钥被附加到加密的反射DLL中,随后是400字节的base64配置文件。

我们开发了以下针对 Brute Ratel v1.1 的工具,用于从 DLL 和 EXE 工件中提取反射 DLL。

本文翻译自:https://www.mdsec.co.uk/2022/08/part-3-how-i-met-your-beacon-brute-ratel/如若转载,请注明原文地址
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论