要点
- Elastic 安全实验室发布了一份关于最近一次攻击活动中的 BUGHATCH 恶意软件分析报告
- 本报告涵盖了详细的代码分析、网络通信协议、命令处理和观察到的 TTP
- 根据这项研究,我们创建了一个 YARA 规则来检测 BUGHATCH 下载器
序言
BUGHATCH 是我们在 2022 年 2 月观察到的古巴勒索软件攻击活动期间部署的自定义 C2 植入程序,该工具很可能是威胁行为者自己构建的,因为它以前没有使用过。
BUGHATCH 能够下载和执行命令以及任意代码,它使操作员可以自由地使用不同的技术执行有效负载,例如反射、shellcode 执行、系统命令执行等等。我们看到的样本没有经过混淆,并且是使用自定义的、经过混淆的 PowerShell 内存加载器部署的,该加载器由 Mandiant 称为 TERMITE。
在本文档中,我们将详细介绍 BUGHATCH 的执行流程,重点介绍其功能和代码执行技术,YARA 规则和 MITRE ATT&CK 映射可在附录中找到。
在此分析中,我们将描述以下内容
- 令牌调整
- 信息收集
- 线程处理和线程同步
- 网络通信协议
- 命令处理
有关古巴勒索软件攻击活动和相关恶意软件分析的信息,请查看我们详细介绍此内容的博客文章
静态分析
| | | | ------------ | ---------------------------------------------------------------- | --- | | SHA256 | F1325F8A55164E904A4B183186F44F815693A008A9445D2606215A232658C3CF | | 文件大小 | 35840 字节 | | 文件类型: | Win32 可执行文件 | | 已签名? | 否 | | 打包器? | 否 | | 编译器 | Visual Studio 2017 - 15.5.0 预览版 2 | | 编译时间 | 2022 年 2 月 6 日 21:05:18 | UTC | | 熵 | 6.109 |
节
名称 | VirtualAddress | 虚拟大小 | 原始大小 | 熵 | MD5 |
.text | 0x1000 | 0x6000 | 0x5400 | 5.933 | A6E30CCF838569781703C943F18DC3F5 |
.rdata | 0x7000 | 0x3000 | 0x2A00 | 6.217 | 9D9AD1251943ECACE81644A7AC320B3C |
.data | 0xA000 | 0x1000 | 0x400 | 1.163 | B983B8EB258220628BE2A88CA44286B4 |
.reloc | 0xB000 | 0x424 | 0x600 | 5.235 | 39324A58D79FC5B8910CBD9AFBF1A6CB |
代码分析
BUGHATCH 是一个内存植入程序,由经过混淆的 PowerShell 脚本加载,该脚本使用常见的 Windows API(VirtualAlloc、CreateThread、WaitForSingleObject)在其分配的内存空间中解码并执行嵌入的 shellcode blob。
PowerShell 加载器使用内联 C# 加载 shellcode 注入所需的 API,如下面的伪代码所示。
PowerShell 脚本使用随机函数和变量名进行混淆,并以反向 Base64 格式包含 shellcode。
该脚本首先解码反向 Base64 编码的数据,然后使用 VirtualAlloc 分配内存区域,然后将 shellcode 复制到其中。最后,该脚本通过使用 CreateThread API 创建新线程来执行 shellcode。
shellcode 从 C2 服务器下载另一个 shellcode blob 和加密的 PE 植入程序,第二个 shellcode 会解密并反射性地加载 PE 恶意软件。
本节将深入探讨 BUGHATCH 的执行流程、线程处理和加密实现、与 C2 的通信协议,最后是支持的命令和实现的有效负载执行技术。
以下是总结植入程序执行流程的图表
令牌调整
植入程序首先使用 SeDebugPrivilege 方法提升权限,使恶意软件能够访问和读取其他进程的内存。它利用常见的 Windows API 来实现此目的,如下面的伪代码所示
信息收集
恶意软件会收集用于对受感染系统进行指纹识别的基于主机的信息,此信息将存储在自定义结构中,该结构将经过 2 字节 XOR 加密并通过 HTTP POST 请求发送到 C2 服务器。
以下列出了收集的信息
- 性能计数器的当前值
- 网络信息
- 系统信息
- 令牌信息
- 当前进程的域和用户名
- 当前进程路径
性能计数器的当前值
使用 QueryPerformanceCounter API,它会收集自系统上次启动以来的时间量。此值将用于计算 2 字节 XOR 加密密钥,以加密植入程序和 C2 服务器之间的通信,稍后将对加密实现进行详细分析。
网络信息
它使用 GetIpAddrTable Windows API 收集连接到受感染计算机的网络接口的地址。
系统信息
BUGHATCH 会收集关键的系统信息,其中包括
- Windows 主版本、次版本和内部版本号
- 处理器体系结构(32 位或 64 位)
- 计算机名称
令牌信息
代理程序会继续收集当前进程令牌组的成员资格,它会调用 AllocateAndInitializeSid API,然后调用 CheckTokenMembership API,连接进程令牌所属的每个组的 SDDL SID 字符串。虽然 BUGHATCH 并非独有,但 Elastic 的 枚举特权本地组的成员资格 检测规则会检测到这一点。
当前进程的域和用户名
恶意软件使用 OpenProcessToken 打开当前进程的句柄,并使用 GetTokenInformation 获取包含令牌用户帐户的结构。然后,它使用 LookupAccountSidW API 检索用户帐户的用户名和域,并将这两个字符串连接成以下格式:DOMAIN\USERNAME。
当前进程路径
最后,它使用 GetModuleFileNameW 收集当前进程路径。然后,恶意软件会使用简单的 2 字节 XOR 算法加密整个填充的结构,此加密实现将在本报告的后面详细介绍。
线程处理和线程同步
植入程序是多线程的;它使用两个不同的链表,一个填充从 C2 服务器接收的命令,另一个填充执行的命令的输出。
它会生成 5 个工作线程,每个线程通过使用 CriticalSection 对象访问相应的链表来处理从 C2 服务器接收的命令。主进程的线程还会出于同步目的,使用 CriticalSection 对象从第二个链表检索命令的输出,以避免任何竞争条件。
网络通信协议
在本节中,我们将详细介绍
- 基本通信协议
- 加密实现
我们分析的植入程序使用 HTTP(S) 进行通信。除了协议的 SSL 加密之外,恶意软件和 C2 还会使用恶意软件为每个新会话计算的 2 字节 XOR 密钥加密数据。用于计算 2 字节 XOR 密钥的值会预先添加到基本协议数据包的开头,服务器会提取这些值来解密/加密命令。
启动后,恶意软件会首先向 C2 服务器发送一个 HTTP POST 请求,其中包含从受害者机器提取的所有收集的信息,如果可用,C2 会以操作员的命令响应,否则代理会休眠 60 秒。在执行命令后,仅当执行的命令的输出可用时,恶意软件才会发送包含收集的信息和命令输出的 POST 请求,否则,它会发送收集的信息并等待新命令。
基本通信协议
BUGHATCH 的作者们实现了一种自定义网络协议,以下是代理和服务器用于通信的语法
- XOR 密钥值: 用于计算 2 字节 XOR 加密密钥的值,该密钥用于加密其余数据
- 分隔符: 一个静态值 (0x389D3AB7),用于分隔 Msg 块,例如:服务器可以在同一个 HTTP 请求中发送不同的指令,这些指令由 分隔符 分隔
- 块长度: 是 Msg、分隔符 和 块长度 的长度
- Msg: 这是要发送的消息,消息内容因代理和服务器而异。
我们将深入探讨代理和服务器的 Msg 封装。
加密实现
该恶意软件在与 C&C 服务器通信时使用 2 字节的 XOR 加密;对于与 C2 服务器的每个会话,植入程序都会生成并计算一个 2 字节的 XOR 密钥。
如前所述,代理使用 QueryPerformanceCounter API 返回的两个 DWORD 值,然后通过 XOR 编码 DWORD 值,再乘以和加上硬编码的值,计算出一个 2 字节的 XOR 密钥。以下是 Python 伪代码,说明了密钥的计算方式:
tmp = (PerformanceCount[0] ^ PerformanceCount[1]) & 0xFFFFFFFF
XorKey = (0x343FD * tmp + 0x269EC3)& 0xFFFFFFFF
XorKey = p16(XorKey >> 16).ljust(2, b'\x00')
命令处理
在本节中,我们将深入探讨代理中实现的功能以及它们各自的 Msg 结构,这些结构将封装在之前提到的基本通信协议结构中。
一旦工作线程启动,主线程将继续向 C2 服务器发送信标以检索命令。主循环由以下步骤组成:
- 发送 POST 请求
- 解密接收到的命令并将其添加到链表
- 休眠 60 秒
工作线程将首先执行 RemoveEntryRecvLinkedList 函数,该函数访问并从链表中检索 C2 服务器发送的数据。
然后,该线程将解封装从 C2 收到的数据,并提取 Msg(Command)。恶意软件根据命令标志实现不同的功能,下表说明了每个命令的功能:
命令标志 | 描述 |
1 | 与代码和命令执行相关的功能组 |
2 | 与模拟和迁移等实用程序相关的功能组 |
3 | 在挂起的子进程中注入 PE 文件 |
命令 1
此命令允许访问与有效负载执行相关的功能,包括从 DLL 到 PE 可执行文件再到 PowerShell 和 cmd 脚本。
一些子命令使用管道来重定向子进程的标准输入/输出,这使攻击者能够执行有效负载并检索其输出,例如 PowerShell 或 Mimikatz 等...
以下是子命令列表:
子命令标志 | 函数名称 | 功能描述 |
2 | ReflectivelyExecutePERemote | 在子进程中反射式加载 PE 文件并重定向其标准输入输出,输出将发送到操作员 C2 服务器 |
3 | DropPEDiskExecute | 将 PE 文件丢弃到磁盘并执行它,执行输出然后发送到操作员的 C2 服务器 |
4 | SelfShellcodeExecute | 在同一进程中执行 shellcode |
5 | RemoteShellcodeExecute | 在挂起的生成的子进程中执行 shellcode |
6 | ExecuteCmd | 执行 CMD 脚本/命令 |
7 | ExecutePowershell | 执行 Powershell 脚本/命令 |
9 | ReflectivelyLoadDllRemote | 使用 CreateRemoteThread API 在远程进程中反射式执行 DLL |
以下是上述命令使用的结构:
struct ExecutePayloadCommandStruct
{
DWORD commandFlag;
DWORD field_0;
DWORD subCommandFlag_1;
DWORD readPipeTimeOut_2;
DWORD payloadSize_3;
DWORD commandLineArgumentSize_4;
DWORD STDINDataSize_5;
CHAR payload_cmdline_stdin[n];
};
- commandFlag: 指示命令
- subCommandFlag: 指示子命令
- readPipeTimeOut: 指示从管道读取子进程输出的超时时间
- payloadSize: 指示有效负载大小
- commandLineArgumentSize: 指示执行有效负载时命令行参数的长度,例如 PE 二进制文件
- STDINDataSize: 指示将发送到子进程的标准输入数据的长度
- Payload_cmdline_stdin: 可以包含有效负载 PE 文件,例如,它的命令行参数以及将转发到子进程的标准输入数据,恶意软件通过它们各自的长度知道每一个的开始和结束。
ReflectivelyExecutePERemote
代理将 PE 二进制文件反射式加载到处于挂起状态的创建进程(cmd.exe 或 svchost.exe)的内存空间中。代理利用 Windows 中的匿名(未命名)管道来重定向创建的子进程的标准输入和输出句柄。它首先创建一个匿名管道,该管道将用于检索创建进程的输出,然后管道句柄在子进程的 STARTUPINFO 结构中指定。
创建挂起进程后,恶意软件会分配一个大的内存块来写入 shellcode 和 XOR 加密的 PE 文件。
shellcode 将进行 2 字节的 XOR 解密并加载嵌入的 PE(类似于 命令 3)。此命令可以加载 64 位和 32 位二进制文件,每个架构都有自己的 shellcode PE 加载器,在注入 shellcode 后,它会将子进程线程的指令指针指向 shellcode 并恢复线程。
以下是从我们的自定义模拟 C2 服务器捕获的数据包示例,我们可以在左侧看到之前讨论的结构,在右侧看到数据包字节,对于恶意软件中实现的每个命令,都会提供一个数据包示例。
DropPEDiskExecute
使用此子命令,操作员可以将 PE 文件丢弃到磁盘并执行它。代理根据 PE 文件类型(GUI 应用程序、CUI(控制台应用程序)或 DLL)具有 3 种不同的实现。
对于 CUI 二进制文件,恶意软件首先在临时文件夹中生成一个随机路径,并使用 CreateFileA 和 WriteFile API 将 PE 文件写入其中。
然后,它通过重定向其标准输入和输出句柄,将丢弃的二进制文件创建一个进程作为子进程;执行有效负载后,输出将发送到操作员的 C2 服务器。
对于 GUI PE 二进制文件,代理只需将其写入磁盘并使用 CreateProcessA API 直接执行它。
最后,对于 DLL PE 文件,恶意软件首先将 DLL 写入临时文件夹中随机生成的路径,然后使用 c:\windows\system32\rundll32.exe 或 c:\windows\syswow64\rundll32.exe(取决于 DLL 的架构)来运行操作员指定的导出函数或函数 start (如果没有指定导出函数)。
SelfShellcodeExecute
此子命令指示代理通过使用 VirtualAlloc API 分配内存区域,然后将 shellcode 复制到其中,在其自己的内存空间中执行 shellcode,通过使用 CreateThread API 创建线程来执行 shellcode。
RemoteShellcodeExecute
此子命令可用于在另一个进程的内存空间中执行 32 位或 64 位位置无关的 shellcode。
与 SpawnAgent 子命令类似,恶意软件使用 CreateProcessA API 创建一个挂起的 svchost.exe 进程,使用 VirtualAllocEx 为 C2 服务器发送的 shellcode 分配一个内存区域,并使用 WriteProcessMemory 写入该区域,然后使用 SetThreadContext 将挂起线程的指令指针设置为指向注入的 shellcode,最后使用 ResumeThread 恢复线程以执行有效负载。
ExecuteCmd 和 ExecutePowershell
操作员可以在受感染的机器上执行 PowerShell 脚本或 CMD 脚本,恶意软件可以将脚本写入临时文件夹中的文件,并带有随机生成的名称,如下所示:PowerShell 的 TEMP<digits>.PS1
或命令 shell 的 TEMP<digits>.CMD
。然后,如果恶意行为者指定,恶意软件会将参数传递给它并执行它,恶意软件使用命名管道来检索 PowerShell 进程的输出。
ReflectivelyLoadDllRemote
在挂起状态下创建的进程中反射式执行 32 位或 64 位 DLL,以下总结了执行流程:
- 检查 PE 文件是 32 位还是 64 位 DLL
- 创建一个挂起的 svchost.exe 进程
- 如果 C2 命令指定,则使用 VirtualAllocEx API 为 DLL 和 DLL 的参数分配内存
- 如果指定,则使用 WriteProcessMemory API 将 DLL 和参数写入远程分配的内存中
- 使用 CreateRemoteThread API 创建远程线程以执行注入的 DLL
命令 2
命令 2 具有多个子功能,如上表所示,根据 subCommandFlag,恶意软件可以执行 6 种不同的操作,如下所示:
子命令标志 | 函数名称 | 功能描述 |
1 | ExitProcess | 退出进程 |
2 | SelfDeleteExitProcess | 自删除并退出进程 |
3 | SpawnAgent64 | 生成 64 位代理 |
4 | SpawnAgent32 | 生成 32 位代理 |
0x1001 | ImpersonateToken | 模拟资源管理器 |
0x1002 | MigrateC2 | 更改 C2 配置 |
以下是上述命令使用的结构:
struct ImpersonateReplicateStruct
{
int subCommandFlag;
int impersonateExplorerToken;
char padding[16];
__int16 isParameterSet;
WCHAR w_parameters[n];
};
ExitProcess
调用 ExitProcess(0) API 终止。
SelfDeleteExitProcess
代理使用 GetModuleFileNameA 获取当前进程的路径,然后执行以下命令进行自删除:使用 CreateProcessA 的 cmd.exe /c del FILEPATH \>\> NUL,然后直接使用 ExitProcess(0) 退出进程。
SpawnAgent64 和 SpawnAgent32
当指定子命令 3 或 4 时,恶意软件将在同一台机器上生成另一个代理,具体取决于 C2 发送的子命令,如上表所示。
恶意软件首先检索嵌入其中的 C2 IP 地址,然后执行 HTTP GET 请求以 shellcode 格式下载打包的代理,在我们分析的示例中,URI /Agent32.bin 用于 32 位代理,/Agent64.bin 用于 64 位代理。
然后,恶意软件使用 CreateProcessA API 创建一个挂起的 svchost.exe 进程,将代理 shellcode 写入该进程,使用 SetThreadContext 将其指令指针设置为指向注入的 shellcode,最后使用 ResumeThread 恢复线程以执行注入的有效负载。
ImpersonateToken
此子命令特定于进程令牌;攻击者可以模拟 explorer.exe 令牌,也可以从 C2 发送的凭据(域\用户名、密码)创建令牌,以生成当前进程的另一个实例。
它将首先检查当前进程是否为本地系统帐户、本地服务帐户或网络服务帐户,方法是测试给定进程令牌是否为具有指定 RID(分别为 SECURITY_LOCAL_SYSTEM_RID、SECURITY_LOCAL_SERVICE_RID、SECURITY_NETWORK_SERVICE_RID)的组的成员。
然后,如果操作员指定了凭据,恶意软件将首先使用域\用户和密码调用 LogonUserW 来创建令牌,然后使用此令牌生成当前进程的另一个实例。
如果未指定,则植入程序将通过使用 DuplicateTokenEx 复制其令牌来模拟 explore.exe 进程,然后,如果没有指定凭据,则使用复制的令牌生成当前进程。
MigrateC2
操作员可以通过指定子命令 0x1001 和新 C2 的 IP 地址,将植入程序迁移到另一个 C2 服务器。
命令 3
当收到命令 3 时,恶意软件会将嵌入在 C&C 请求中作为有效负载的 PE 文件反射式加载到另一个进程的内存空间中,以下是执行的概述:
- 确定 PE 文件的类型和架构
- 创建挂起的进程
- 在挂起的进程中分配大内存
- 在分配的内存中写入一段 shellcode,该 shellcode 将定位、解密并反射加载 PE 文件。
- 对 PE 文件进行 2 字节的 XOR 加密,并将其附加到 shellcode 之后。
- 设置已暂停进程的 EIP 上下文,以执行 shellcode。
shellcode 随后将反射加载 PE 文件。
代理首先解析从 C2 服务器接收到的 PE 文件,以确定 PE 文件的类型和架构。
然后根据此信息,将选择一个 Windows 签名可执行文件进行注入。
如果 PE 文件是 CUI(控制台用户界面),恶意软件将选择 cmd.exe;但是,如果它是 GUI(图形用户界面)或 DLL PE 文件,它将选择 svchost.exe。
恶意软件随后使用 CreateProcessA API (cmd.exe 或 svchost.exe) 创建一个暂停进程,并使用 VirtualAllocEx 在创建的进程中分配大量内存。然后,它将存储在 .rdata 部分中的与位置无关的 shellcode 复制到新分配的内存中,该 shellcode 负责根据特定标签定位附加的 PE 文件,对其进行解密并在内存中反射加载。
然后,它在 shellcode 之后附加一个 12 字节的结构,该结构由一个标签、PE 文件的大小和一个 2 字节的 XOR 密钥组成。
它将对 PE 文件进行 2 字节的 XOR 加密,并将其附加到该结构之后。以下是写入分配内存的数据概述:
SHELLCODE | 标签 | PE 大小 | 2 字节 XOR 密钥 | 2 字节 XOR 加密的 PE 文件 |
代理随后将使用 SetThreadContext 设置线程上下文,并将暂停进程的指令指针指向 shellcode,然后使用 ResumeThread 简单地恢复执行。
shellcode 将首先根据标签值 (0x80706050) 定位 2 字节 XOR 加密的 PE 文件,然后对其进行 2 字节 XOR 解密,并在同一进程内存中反射加载。
观察到的对手战术和技术
Elastic 使用 MITRE ATT&CK 框架来记录高级持续性威胁对企业网络使用的常见战术、技术和规程。
战术
战术代表技术或子技术的原因。它是对手的战术目标:执行操作的原因。
技术/子技术
技术和子技术代表对手如何通过执行操作来实现战术目标。
检测
检测规则
在分析 BUGHATCH 样本期间观察到以下检测规则。此规则并非 BUGHATCH 活动独有。
YARA 规则
Elastic Security 创建了一个 YARA 规则 来识别此活动。
rule Windows_Trojan_BUGHATCH {
meta:
author = “Elastic Security”
creation_date = "2022-05-09"
last_modified = "2022-06-09"
license = “Elastic License v2”
os = "Windows"
arch = "x86"
category_type = "Trojan"
family = "BUGHATCH"
threat_name = "Windows.Trojan.BUGHATCH"
reference_sample = "b495456a2239f3ba48e43ef295d6c00066473d6a7991051e1705a48746e8051f"
strings:
$a1 = { 8B 45 ?? 33 D2 B9 A7 00 00 00 F7 F1 85 D2 75 ?? B8 01 00 00 00 EB 33 C0 }
$a2 = { 8B 45 ?? 0F B7 48 04 81 F9 64 86 00 00 75 3B 8B 55 ?? 0F B7 42 16 25 00 20 00 00 ?? ?? B8 06 00 00 00 EB ?? }
$a3 = { 69 4D 10 FD 43 03 00 81 C1 C3 9E 26 00 89 4D 10 8B 55 FC 8B 45 F8 0F B7 0C 50 8B 55 10 C1 EA 10 81 E2 FF FF 00 00 33 CA 8B 45 FC 8B 55 F8 66 89 0C 42 }
$c1 = "-windowstyle hidden -executionpolicy bypass -file"
$c2 = "C:\\Windows\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe"
$c3 = "ReflectiveLoader"
$c4 = "\\Sysnative\\"
$c5 = "TEMP%u.CMD"
$c6 = "TEMP%u.PS1"
$c7 = "\\TEMP%d.%s"
$c8 = "NtSetContextThread"
$c9 = "NtResumeThread"
condition:
any of ($a*) or 6 of ($c*)
}