Salim Bitam

BUGHATCH 恶意软件分析

BUGHATCH 下载器的恶意软件分析。

阅读 34 分钟恶意软件分析
BUGHATCH Malware Analysis

要点

  • 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
.text0x10000x60000x54005.933A6E30CCF838569781703C943F18DC3F5
.rdata0x70000x30000x2A006.2179D9AD1251943ECACE81644A7AC320B3C
.data0xA0000x10000x4001.163B983B8EB258220628BE2A88CA44286B4
.reloc0xB0000x4240x6005.23539324A58D79FC5B8910CBD9AFBF1A6CB

代码分析

BUGHATCH 是一个内存植入程序,由经过混淆的 PowerShell 脚本加载,该脚本使用常见的 Windows API(VirtualAllocCreateThread、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 等...

以下是子命令列表:

子命令标志函数名称功能描述
2ReflectivelyExecutePERemote在子进程中反射式加载 PE 文件并重定向其标准输入输出,输出将发送到操作员 C2 服务器
3DropPEDiskExecute将 PE 文件丢弃到磁盘并执行它,执行输出然后发送到操作员的 C2 服务器
4SelfShellcodeExecute在同一进程中执行 shellcode
5RemoteShellcodeExecute在挂起的生成的子进程中执行 shellcode
6ExecuteCmd执行 CMD 脚本/命令
7ExecutePowershell执行 Powershell 脚本/命令
9ReflectivelyLoadDllRemote使用 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.exesvchost.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 二进制文件,恶意软件首先在临时文件夹中生成一个随机路径,并使用 CreateFileAWriteFile API 将 PE 文件写入其中。

然后,它通过重定向其标准输入和输出句柄,将丢弃的二进制文件创建一个进程作为子进程;执行有效负载后,输出将发送到操作员的 C2 服务器。

对于 GUI PE 二进制文件,代理只需将其写入磁盘并使用 CreateProcessA API 直接执行它。

最后,对于 DLL PE 文件,恶意软件首先将 DLL 写入临时文件夹中随机生成的路径,然后使用 c:\windows\system32\rundll32.exec:\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 种不同的操作,如下所示:

子命令标志函数名称功能描述
1ExitProcess退出进程
2SelfDeleteExitProcess自删除并退出进程
3SpawnAgent64生成 64 位代理
4SpawnAgent32生成 32 位代理
0x1001ImpersonateToken模拟资源管理器
0x1002MigrateC2更改 C2 配置

以下是上述命令使用的结构:

struct ImpersonateReplicateStruct
{
  int subCommandFlag;
  int impersonateExplorerToken;
  char padding[16];
  __int16 isParameterSet;
  WCHAR w_parameters[n];
};

ExitProcess

调用 ExitProcess(0) API 终止。

SelfDeleteExitProcess

代理使用 GetModuleFileNameA 获取当前进程的路径,然后执行以下命令进行自删除:使用 CreateProcessAcmd.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_RIDSECURITY_LOCAL_SERVICE_RIDSECURITY_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.exesvchost.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*)
}