Cyril FrançoisSamir Bousseaden

解剖 REMCOS RAT:深入分析 2024 年广泛传播的恶意软件,第二部分

第二部分:深入探讨 REMCOS 的录制功能、启动和 C2 通信

阅读 8 分钟恶意软件分析
Dissecting REMCOS RAT: An in-depth analysis of a widespread 2024 malware, Part Two

在本系列关于 REMCOS 植入程序的上一篇文章中,我们分享了有关执行、持久性和防御规避机制的信息。在本系列的续集中,我们将介绍其执行流程的后半部分,您将了解有关 REMCOS 录制功能以及与其 C2 通信的更多信息。

启动看门狗

如果enable_watchdog_flag(索引0x32)已启用,则 REMCOS 将激活其看门狗功能。

此功能涉及恶意软件启动一个新进程,将自身注入其中,并监视主进程。看门狗的目标是在主进程终止时重新启动它。主进程也可以在自身终止时重新启动看门狗。

看门狗注入的目标二进制文件是从硬编码列表中选择的,选择第一个成功创建进程和注入的二进制文件

  • svchost.exe
  • rmclient.exe
  • fsutil.exe

在此示例中,看门狗进程是svchost.exe

在启动看门狗进程之前,会创建注册表值HKCU/SOFTWARE/{MUTEX}/WD,其中包含主进程 PID。

一旦 REMCOS 在看门狗进程中运行,它就会通过验证恶意软件注册表项中是否存在WD值来采取“特殊”执行路径。如果存在,则删除该值并调用监视过程函数。

值得注意的是,看门狗进程有一个特殊的互斥体,以将其与主进程互斥体区分开来。此互斥体字符串派生自配置(索引0xE)并在其后附加-W

当主进程终止时,看门狗会检测到并使用ShellExecuteW API 重新启动它,该 API 使用从HKCU/SOFTWARE/{mutex}/exepath注册表项检索到的恶意软件二进制文件的路径。

启动录制线程

键盘记录线程

离线键盘记录器有两种操作模式

  1. 记录所有按键
  2. 在特定窗口处于前台时启用键盘记录

keylogger_mode(索引0xF)字段在配置中设置为 1 或 2 时,REMCOS 会激活其“离线键盘记录器”功能。

键盘记录是使用SetWindowsHookExA API 和WH_KEYBOARD_LL常量完成的。

存储键盘记录数据的文件使用以下配置字段构建

  • keylogger_root_directory(索引0x31
  • keylogger_parent_directory(索引0x10
  • keylogger_filename(索引0x11

键盘记录器文件路径为{keylogger_root_directory}/{keylogger_parent_directory}/{keylogger_filename}。在这种情况下,它将为%APPDATA%/keylogger.dat

可以通过在配置中启用enable_keylogger_file_encryption_flag(索引0x12)标志来加密键盘记录器文件。它将使用 RC4 算法和配置密钥进行加密。

还可以通过在配置中启用enable_keylogger_file_hiding_flag(索引0x13)标志来使文件超级隐藏。

使用第二种键盘记录模式时,需要使用字符串设置keylogger_specific_window_names(索引0x2A)字段,这些字符串将在每 5 秒钟检查当前前台窗口标题。

匹配后,键盘记录开始。随后,每秒钟检查当前前台窗口,如果标题不再包含指定的字符串,则停止键盘记录器。

屏幕录制线程

enable_screenshot_flag(索引0x14)在配置中启用时,REMCOS 将激活其屏幕录制功能。

要截取屏幕截图,REMCOS 利用CreateCompatibleBitmapBitBlt Windows API。如果enable_screenshot_mouse_drawing_flag(索引0x35)标志已启用,则还使用GetCursorInfoGetIconInfoDrawIcon API 在位图上绘制鼠标。

存储屏幕截图的文件夹的路径使用以下配置构建

  • screenshot_parent_directory(索引0x19
  • screenshot_folder(索引0x1A

最终路径为{screenshot_parent_directory}/{screenshot_folder}

REMCOS 利用screenshot_interval_in_minutes(索引0x15)字段每 X 分钟捕获一次屏幕截图并使用以下格式字符串将其保存到磁盘:time_%04i%02i%02i_%02i%02i%02i

与键盘记录数据类似,当enable_screenshot_encryption_flag(索引0x1B)启用时,屏幕截图将使用 RC4 加密算法和配置密钥进行加密保存。

在顶部,REMCOS 具有与其键盘记录功能类似的“特定窗口”屏幕录制功能。当enable_screenshot_specific_window_names_flag(索引0x16)设置时,将启动第二个屏幕录制线程。

这次,它利用screenshot_specific_window_names(索引0x17)字符串列表,当前台窗口标题包含指定字符串之一时捕获屏幕截图。屏幕截图每 X 秒拍摄一次,由screenshot_specific_window_names_interval_in_seconds(索引0x18)字段指定。

在这种情况下,屏幕截图使用不同的格式字符串保存在磁盘上:wnd_%04i%02i%02i_%02i%02i%02i。下面是一个使用 ["notepad"] 作为特定窗口名称列表并在前台设置记事本进程窗口的示例。

音频录制线程

enable_audio_recording_flag(索引0x23)启用时,REMCOS 将启动其音频录制功能。

录制使用 Windows Wave* API 进行。录制时长由audio_recording_duration_in_minutes0x24)配置字段以分钟为单位指定。

录制 X 分钟后,录制文件将保存,并开始新的录制。REMCOS 使用以下配置字段构建录制文件夹路径

  • audio_record_parent_directory(索引0x25
  • audio_record_folder(索引0x26

最终路径为{audio_record_parent_directory}/{audio_record_folder}。在这种情况下,它将是C:\MicRecords。录制使用以下格式保存到磁盘:%Y-%m-%d %H.%M.wav

与 C2 通信

初始化后,REMCOS 将启动与其 C2 的通信。它尝试连接到其c2_list(索引0x0)中的每个域名,直到其中一个做出响应。

根据之前的研究,如果为特定 C2 启用了加密,则通信可以使用 TLS 进行加密。在这种情况下,TLS 引擎将利用tls_raw_certificate(索引0x36)、tls_key(索引0x37)和tls_raw_peer_certificate(索引0x38)配置字段来建立 TLS 隧道。

需要注意的是,在这种情况下,只能为多个启用 TLS 的 C2 域名提供一个对等证书。因此,可以使用相同的证书识别其他 C2。

连接后,我们收到了第一个数据包。

Fortinet 深入描述,协议没有改变,所有数据包都遵循相同的结构。

  • (橙色)magic_number\x24\x04\xff\x00
  • (红色)data_size\x40\x03\x00\x00
  • (绿色)command_id(数字):\0x4b\x00\x00\x00
  • (蓝色)数据字段由|\x1e\x1e\1f|分隔。

在从恶意软件接收第一个数据包后,我们可以使用以下函数发送自己的命令。

MAGIC = 0xFF0424
SEPARATOR = b"\x1e\x1e\x1f|"


def build_command_packet(command_id: int, command_data: bytes) -> bytes:
	return build_packet(command_id.to_bytes(4, byteorder="little") + command_data)


def build_packet(data: bytes) -> bytes:
	packet = MAGIC.to_bytes(4, byteorder="little")
	packet += len(data).to_bytes(4, byteorder="little")
	packet += data
	return packet

这里我们将使用命令 0x94 更改记事本窗口的标题,并将窗口句柄 (329064) 和我们选择的文本作为参数传递。

def main() -> None:
	server_0 = nclib.TCPServer(("192.168.204.1", 8080))

	for client in server_0:
    	print(client.recv_all(5))

    	client.send(build_command_packet(
            			0x94,
            			b"329064" + SEPARATOR + "AM_I_A_JOKE_TO_YOU?".encode("utf-16-le")))

这是第二篇文章的结尾。第三部分将涵盖 REMCOS 的配置及其 C2 命令。