文章总结: 本文详述了利用GitHub仓库抢注分发恶意软件的攻击。攻击者通过Fork机制伪造安装程序,利用单文件.NET捆绑及OpenCL反分析技术规避检测,通过DLL侧加载释放HijackLoader。文章提供详细技术分析、IoC与YARA规则,建议从官方Releases下载软件。
综合评分: 100
文章分类: 恶意软件,逆向分析,威胁情报,免杀,漏洞分析
仓库抢注:Docker Desktop官方仓库成恶意软件分发渠道
Dubito
Dubito
云原生安全指北
2026年1月28日 09:08
江苏
注:本文翻译自 GMO Cybersecurity 的文章《Revisiting GPUGate: Repo Squatting and OpenCL Deception to Deliver HijackLoader》[1],可点击文末“阅读原文”按钮查看英文原文。
全文如下:
一、引言
在之前的一份(日语)报告[2]中,我们描述了攻击者如何劫持官方的 GitHub Desktop[3] 仓库来分发伪装成 GitHub Desktop 安装程序的恶意软件。此次攻击之所以能够实现,是因为 GitHub 的设计机制允许用户:fork 一个公共仓库,在自己的分支中进行提交,然后通过上游仓库查看该提交。这实际上使得用户的提交可以出现在官方仓库的命名空间下,即使他们对该仓库没有直接的 write 权限。我们将此技术称为 repo squatting(仓库抢注)。
2025年9月9日,GitHub 声明其安全团队已注意到此问题,并正在采取措施进行缓解。然而,截至2025年12月29日,该问题仍可复现。
GMO Cybersecurity by Ierae, Inc. 正在追踪这个持续进行且不断演变的攻击活动。其恶意安装程序充当多阶段加载器,用于投放 HijackLoader。在本报告中,我们将重新审视仓库抢注技术,分享我们对恶意软件的技术分析,并澄清一些围绕基于 GPU 的反分析行为(被称为 “GPUGate”[4])的误解。
GMO Ierae 利用本研究结果来提升我们产品的安全性,保护客户免受本报告所述攻击和威胁的侵害。所有已识别的域名和文件哈希值已在报告末尾分享。
二、关键要点
-
• 攻击活动在2025年9月至10月期间最为活跃。
-
• 观察到主要针对欧盟/欧洲经济区的恶意广告,但在日本也有感染发生。
-
• 目标是搜索开发者工具的用户。
-
• 滥用 GitHub 的 fork 网络提交可见性机制,通过提交哈希(commit hashes)“抢注”在官方仓库命名空间下。
-
• 通过伪装成合法安装程序的多阶段加载器投放 HijackLoader;macOS 受害者则会收到 AMOS 窃密木马。
-
• 使用基于 GPU 的 API(OpenCL[5])来阻碍通常缺乏 GPU 驱动或 OpenCL 运行时的沙箱/虚拟机中的动态分析。在我们的案例中,这迫使我们的分析工作转移到配备 GPU 的物理机上,以便加载器能够完整运行。
-
• 通过代码误导手段,蓄意诱导分析人员,使静态恢复解密密钥的过程复杂化。
三、恶意软件分发:恶意广告 + GitHub 仓库抢注
为简要总结我们之前的报告,以下是恶意 GitHub Desktop 安装程序在 2025 年 9 月攻击活动中的分发方式。
步骤 1. 攻击者创建一个一次性 GitHub 账户,并 fork 官方的 GitHub Desktop 仓库。
步骤 2. 攻击者编辑 README 文件中的下载链接,指向其恶意安装程序,并提交此更改。该提交哈希(commit hash)可在官方仓库的命名空间下被查看:
github.com/desktop/desktop/tree/<commit_hash>
此行为是 GitHub 有意设计的,并在 GitHub 官方文档[6] 中有所说明。然而,它也允许攻击者通过恶意提交来“抢注”官方仓库。GitHub 的设计还意味着,即使你删除了 fork 或创建它的账户,该提交哈希仍可用于访问该提交。这是因为该提交仍然是仓库网络的一部分,这使得追踪和清理恶意提交变得异常困难。
步骤 3. 最后,攻击者使用“GitHub Desktop”的赞助广告来推广其提交,通过 README.md 中的锚点来绕过 GitHub 的警告:
github.com/desktop/desktop/tree/<commit_hash>?tab=readme-ov-file#where-can-i-get-it
下载此恶意 Windows 安装程序的受害者将执行一个多阶段加载器,而 Mac 受害者则会收到 AMOS 窃密木马。
四、感染链概述
在下一节中,我们将更详细地分析 GitHubDesktopSetup-x64.exe 攻击活动中感染链的关键组件。总体而言,其感染链如下:
五、阶段 1:恶意安装程序(单文件 .NET 加载器)
- • 文件名:
GitHubDesktopSetup-x64.exe - • SHA256 哈希值:
e252bb114f5c2793fc6900d49d3c302fc9298f36447bbf242a00c10887c36d71 - • 磁盘大小:127.68 MB
GMO Ierae 于 2025 年 9 月初首次观测到恶意的 GitHubDesktopSetup-x64.exe 安装程序。然而,类似的样本最早可追溯至 2025 年 5 月,它们伪装成其他安装程序名称,例如 Сhromе-x64.exe、Notiоn-x64.exe、1Passwоrd-x64.exe 和 Вitwаrdеn-x64.exe。
从表面上看,GitHubDesktopSetup-x64.exe 像是一个典型的 C++ 应用程序。然而,其调试信息揭示了其真实的文件类型:
PDB: D:\a\_work\1\s\artifacts\obj\coreclr\windows.x64.Release\Corehost.Static\singlefilehost.pdb
singlefilehost.pdb 是单文件 .NET 应用程序的程序数据库(PDB,Program Database)文件。单文件 .NET 应用程序是指其所有依赖项都捆绑进一个名为 AppHost 的单独可执行文件中的 .NET 应用程序。AppHost 实质上是 .NET 应用程序的启动器,其名称由开发者指定;在我们的案例中,它就是 GitHubDesktopSetup-x64.exe。这种捆绑过程由 bundler[7] 工具处理,该工具会 “将托管应用程序及其依赖项作为二进制数据块附加在 AppHost 可执行文件的末尾。”[8] 因此,托管 .NET 有效负载可以在覆盖数据区中找到。在本样本中,它位于偏移量 0x00000800–0x03C04800 之间。
注意:覆盖数据区还包含许多其他 PE 可执行文件。这些文件的存在仅仅是为了模仿合法 GitHub Desktop 安装程序的大小,恶意软件本身并不会使用它们。
您可以将这些字节数据转储到磁盘,然后用 dnSpy 打开结果文件进行分析。在此之前,我们先简要说明如何识别单文件应用程序。
单文件应用程序可以通过一个名为 bundle marker[9] 的静态变量来识别,该变量会在 .NET 应用程序的启动序列中被检查:
执行时,AppHost 会检查这个 bundle marker 以确定可执行文件是否为单文件捆绑包。此结构由一个 8 字节的 bundle header-offset 后跟一个固定的 32 字节标记(即 .NET 核心捆绑包的 “bundle signature(捆绑签名)”)组成。如果 bundle header-offset 字段非零,AppHost 就将该可执行文件视为单文件捆绑包,并使用该偏移量来定位和解析 bundle header。如果偏移量为零,则将其视为普通的 AppHost,并从磁盘上的单独文件加载托管程序集。
您可以在 GitHubDesktopSetup-x64.exe 内部搜索此 bundle signature 来确认它是一个单文件捆绑包。
紧接在 bundle signature 之前,8 字节的 bundle header-offset 被设置为 0x7FAB159,这确认了这是一个单文件应用程序。此 bundle header-offset 和 signature 可以与其他标识符结合使用,通过 YARA 规则来搜索相关样本。
5.1 阶段 1 .1:内嵌的 .NET 负载
- • SHA-256:
60618979eba9deb762d3c294d5bbc0aecacf19fc3be9b1dfc5fc633556e0cdcb - • 磁盘大小:62,930,929 bytes(60.02 MB)
之前,我们在覆盖数据区的起始部分提取并识别出了一个 .NET 有效负载。这是一个用 C# 编写的 64 位 .NET 应用程序。
该程序集还定义了一个 Resources 类,其中包含两个加密数据块:
- •
EncryptedKernel:709 bytes - •
EncryptedPayload:5648 bytes
这个 .NET 应用程序充当加载器,负责解密并执行第二个 .NET 有效负载(EncryptedPayload)。为了做到这一点,它巧妙地使用了名为 OpenCL[5] 的基于 GPU 的 API,其使用方式既能干扰沙箱/虚拟机的动态分析,又使静态恢复解密密钥的过程变得复杂。
5.2 OpenCL 的诡计
GenerateKey() 方法就是实现这种基于 OpenCL 的误导手段的地方。OpenCL 用于编写小型程序(内核),这些程序由 CPU 调用,但在 GPU 上执行。大多数现代 CPU/GPU 供应商都提供 OpenCL 运行时,并附带 OpenCL 可安装客户端驱动程序 (ICD) 加载器[10],其中包含动态库 OpenCL.dll。在 Windows 上,该文件通常位于 C:\Windows\System32 目录下:
正如 eversinc33 关于 GPU 恶意软件的 研究[11] 所述,OpenCL 可被滥用于基于 GPU 的内存操作(例如,通过 OpenCL 内核进行有效负载解密或数据检索)。乍一看,GenerateKey()似乎 正是这么做的。但实际上,它的实现方式旨在误导分析人员并阻碍解密密钥的静态恢复。
概括来说,GenerateKey() 会:
- • 通过
DllImport加载OpenCL.dll以与 OpenCL API 交互, - • 使用简单的 XOR(
0x5A)解密第一个资源(EncryptedKernel),以恢复内核源代码, - • 在运行时使用
clCreateProgramWithSource[12] 和clBuildProgram[13] 编译内核, - • 通过
clCreateKernel[14] 获取内核函数的句柄, - • 通过
clSetKernelArg[15] 设置函数参数, - • 通过
clEnqueueNDRangeKernel[16] 调用内核, - • 并使用
clEnqueueReadBuffer[17] 将内核的输出从 GPU 内存读回主机 CPU 内存。
以下是解密后的 OpenCL 内核源代码,其语法类似于 C:
generate_key() 接收一个 device_name,并检查设备名称的长度是否至少为 10 个字符。如果长度小于 10,则将一个 fake_key 写入 output:
- •
0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
否则,将一个 good_key 写入 output:
- •
0x123456789ABCDEF01122334455667788
这个密钥 看似 用于在 DecryptPayload() 中解密主有效负载(EncryptedPayload)。
如下图所示,GenerateKey() 多次调用了 clGetPlatformIDs[18] 和 clGetDeviceIDs[19],因此你可能会认为 GPU 设备名称被获取并传递给了内核进行长度检查。然而,这两个密钥都无法解密 EncryptedPayload。这是为什么呢?
首先,clGetPlatformIDs 和 clGetDeviceIDs并不会返回诸如 GeForce RTX 4090 这样的设备字符串。这些字符串是通过 clGetDeviceInfo[20] / clGetPlatformInfo[21] 获取的。如果设备字符串要传递给内核,通常会看到使用 clCreateBuffer 并配合 CL_MEM_COPY_HOST_PTR 来用该主机字符串初始化 GPU 内存。而在这里,调用 clCreateBuffer 时,host_ptr 被设置为 IntPtr.Zero(NULL),因此没有使用主机内存来初始化 device_name(intPtr6)缓冲区。所以,device_name 仅仅是未初始化的内存。
其次,OpenCL 调用以一种完全阻止内核运行的方式失败了。intPtr6 和 intPtr7 是 clCreateBuffer 返回的缓冲区句柄:
- •
intPtr6:device_name的缓冲区句柄 - •
intPtr7:output的缓冲区句柄
clSetKernelArg(用于设置内核参数)期望其最后一个参数是一个指向包含参数值的内存区域的指针(例如,&intPtr6)。而在本例中,两个句柄都是按值传递的(例如,intPtr6)。因此,clSetKernelArg 返回了 0xFFFFFFDA(CL_INVALID_MEM_OBJECT)。
此外,调用内核的 clEnqueueNDRangeKernel 返回了 0xFFFFFFCC(CL_INVALID_KERNEL_ARGS),这证实了内核并未执行,也从未向输出缓冲区写入任何数据。因此,array7 保持其默认的全零值,而 GenerateKey() 返回的密钥是:0x00000000000000000000000000000000
基于 GPU 的解密通常用于阻碍动态分析,但对静态分析的效果往往较差。这个技巧的引入正是为了弥补这一点,并且效果非常显著。这绝非易事。为了完全理解其行为,我们在一台配备 GPU(GeForce RTX 3050 Ti)的物理机上调试了此 .NET 有效负载。
在实践中,OpenCL 仍然会阻碍动态分析。在我们最初尝试在虚拟机监控程序(hypervisor)和沙箱中调试该 .NET 有效负载时,执行很早便失败了,因为缺乏 GPU 供应商驱动程序或 OpenCL 运行时。在这些环境中,C:\Windows\System32 目录下通常没有 OpenCL.dll,首次尝试解析或调用导入的 OpenCL 函数会引发 DllNotFoundException。此外,我们在没有独立 GPU 的笔记本电脑上测试时,调用的第一个 OpenCL API clGetPlatformIDs[18] 返回了 0xFFFFFC17(CL_PLATFORM_NOT_FOUND_KHR),这意味着虽然 OpenCL.dll 存在,但没有可用的供应商特定的 OpenCL 平台(例如 NVIDIA 或 AMD)。
关于基于 GPU 的恶意软件的更多信息,请参阅 eversinc33 的这篇 文章[11]。
5.3 解密 EncryptedPayload
现在我们有了正确的密钥,就可以静态地解密 EncryptedPayload 了。DecryptPayload() 使用 AES-128(密钥为 16 字节),并使用一个 16 字节的零数组作为 IV:
- • IV:
0x00000000000000000000000000000000(aes.IV = new byte[16];) - • 密钥:
0x00000000000000000000000000000000
在 DecryptPayload() 中,没有指定 CipherMode 或 PaddingMode,因此 .NET 默认使用 CBC 模式和 PKCS7 填充(PaddingMode.PKCS7)。
5.4 解密后的 .NET 有效负载
- • SHA-256:
e5c01a6f3d85c469e16857d92d9f0a1b01d14b0f0dad7df94b1afa6dc1ff4490
这个解密后的 .NET 有效负载通过 GetByteArrayAsync[22] 从 slepseetwork[.]online(45[.]59[.]124[.]94:443)获取另一个 .NET 有效负载,并像之前一样执行它。遗憾的是,在该负载被下线之前,我们未能成功获取到它。然而,我们的遥测数据显示,它使用 Windows Script Host(wscript)从 %TEMP% 启动一个 VBScript,该脚本随后会执行下文描述的 PowerShell 加载器。
六、阶段 2:PowerShell 加载器
- • SHA-256:
8cd7d9ccea98ad6a3dfb4767e574349c9fd5678150c629661574ddd45e40cd37
该加载器的行为可概括如下:
首先,它将自身从 %TEMP% 复制到 %AppData%(Roaming)目录。例如:C:\Users\<Username>\AppData\Roaming\Stager.ps1
接着,它重新启动自身,以确保副本在隐藏窗口中运行,并附带参数 --fromloader 和 --detached,同时使用 ExecutionPolicy Bypass(如果尚未提升权限,则会通过 RunAs 请求一次 UAC)。
重新启动后,它会创建标记文件 adm_marker.tmp:C:\Users\<Username>\AppData\Local\Temp\adm_marker.tmp。
此标记用作互斥锁,以阻止后续运行。
然后,脚本为以下路径添加三个 Microsoft Defender 排除项:
- •
AppData - •
LocalAppData - •
ProgramData
随后,它会创建或替换一个名为 WinSvcUpd 的计划任务,以便在任何用户登录时执行该脚本。WinSvcUpd 并非合法的 Windows 任务,且相对独特;相同的任务名称也出现在 ARECHCLIENT2(SectopRAT)样本[23]中,如 Elastic Security Labs[24] 的文档所述。
接下来,它会从攻击者的域名下载 archive.zip:
oqiwquwqey[.]xyz/zipep[.]php
并将其保存为: C:\Users\<Username>\AppData\Local\Temp\archive.zip。
然后,它将压缩包解压到一个随机的 Temp 子文件夹中,例如 tmp100001(tmp + 100000–999999),并执行其中所有的 .exe 文件,这些文件随后将在之前设置的排除项下运行。
在其他样本中,任务名称($taskName)、临时标记($markerPath)和攻击者域名($zipUrl)可能有所不同。本报告末尾提供了其他样本的列表及其差异。在其中两个样本中,可以找到俄语注释:
- •
# Если не админ, запрашиваем один раз UAC и выходим“如果不是管理员,请求一次 UAC 并退出” - •
# Перезапуск в оторванном процессе“在分离的进程中重新启动”
七、阶段 3:DLL 侧加载
如前所述,PowerShell 脚本会下载 archive.zip 并在解压后执行其内容。在本样本中,执行始于 Control-Binary32.exe。
archive.zip:
- • SHA-256:
37d6ba7366cf7efca9693fbf0f9efbd23549895c561c933544fc707d56d13f8a - • 路径:
C:\Users\XXXXX\AppData\Local\Temp\archive.zip - • 磁盘大小:5,323,050 bytes
archive.zip 内包含六个文件:
-
•
Control-Binary32.exe(32-bit) -
• SHA-256:
79384ef76740962757d617bc056bf8a45b2ef8f1e1587632b36830e2fc6ab21a -
•
Qt5Network.dll(32-bit) -
• SHA-256:
719a726d54161a1a95cf69f3001b74fe15661b83d995b89bcca5ecc8e792e2eb -
•
FileAssocation.dll(32-bit) -
• SHA-256:
95d51ee9c58f789213cedac7e82c7ba064364d9e5c8ca76ad27a5e53537f9fdf -
•
Qt5Core.dll(32-bit) -
• SHA-256:
b967ade09a9338320e0db4e5da11a2ac396950f0eed689b28bd31686b7baf018 -
•
Prangshound.hzj -
• SHA-256:
58f897d4369a4c667b2f40a6703c7ae42912a10186d81c6eaa7809513da86a51 -
•
Kraekgriesfid.xvs -
• SHA-256:
be503f616edacac10689b63ba39c4b5d791fcf365bc80a0c8bc27c2c3d3cb2a4
在运行时,Control-Binary32.exe 会从解压后的 Temp 子文件夹(例如 tmp100001)中加载 Qt5Core.dll、FileAssocation.dll 和 Qt5Network.dll。这个子文件夹位于 PowerShell 脚本创建的 Defender 排除路径下,因此这些文件运行时不会被 Microsoft Defender 扫描。实际上,您可能只会看到 Control-Binary32.exe 进程的创建及其 DLL 加载活动。
Prangshound.hzj 和 Kraekgriesfid.xvs 是异常的文件名,它们都作为字符串引用出现在 Qt5Network.dll 内部,这促使我们对二进制文件进行更深入的检查。
乍看之下,Qt5Network.dll 看起来相当正常:执行流从 DLL 入口点经过标准的 CRT 启动过程。然而,在 DLL_PROCESS_ATTACH 期间,CRT 路径最终会到达 __scrt_initialize_default_local_stdio_options,其中有一个本不应存在的 sub_64004E1C 调用。
除了 sub_64004E1C(以及由此衍生的代码)之外,Qt5Network.dll 与合法的二进制文件完全相同。如果您获取原始的 Qt5Network.dll,可以将其与恶意版本进行比对来确认这一点。BinDiff 识别出了两个主要未匹配函数和一个次要未匹配函数:
- •
sub_64004D9C - •
sub_64004E1C - •
sub_64004E90
注意:Control-Binary32.exe[25]、FileAssocation.dll[26] 和 Qt5Core.dll[27] 是合法的已签名二进制文件,并非恶意。
IDA 无法清晰地反编译 sub_64004E1C 周围的代码,因此我们从这一点开始,在 x32DBG 中继续进行分析,以便更好地理解该函数。
八、阶段 4:载荷部署与模块替换
概括来说,sub_64004E1C 负责解密并部署下一个有效负载。它从 Prangshound.hzj 中读取加密的负载,在内存中解密,并将大部分解密后的字节复制到 vssapi.dll(一个合法的 Windows DLL)的 .text 节起始位置。随后,该内存区域被设置为 PAGE_EXECUTE_READWRITE,执行流程跳转至该区域内的偏移 0xED0 处,并传递一个指向剩余解密字节(用作配置)的指针。下文将更详细地解释这些步骤。
在整个函数中,sub_64004E1C 动态解析其所需的 kernel32 API。它从 Prangshound.hzj 中读取 0x5E45 字节到缓冲区,然后前进到缓冲区中偏移 0x462D 的位置(此处称为 allocated_memory_offset_462D)。
对于解密过程,该函数将 allocated_memory_offset_462D 处的头 8 字节解释为:
- •
0x10180000:有效负载长度 - •
0xEE667020:解密密钥
加密的有效负载紧接在这 8 个字节之后开始(allocated_memory_offset_462D + 0x8)。从那里开始,它遍历加密负载的 16 字节块,将 0xEE667020 加到每个 4 字节字(dword)上,并将结果写回原处。用 C 语言表示,其本质是:
for (size_t i = 0; i < payload_length / 4; i++) {
((uint32_t*)payload)[i] += key;
}
如下图所示,解密后的有效负载以字符串 vssapi.dll(用于模块替换的模块)开头。另外两个值也很重要:
- •
0xED0 - •
0x16B0
0xED0 用作从 vssapi.dll 的 .text 节起始处的执行偏移量,而 0x16B0 用作从 shellcode 起始处到配置数据起始处的偏移量,该配置数据将在下一阶段使用。
在 Prangshound.hzj 中,偏移 0x464D(0x462D + 0x20)标志着 shellcode 的开始,偏移 0x5CFD(0x462D + 0x16B0)标志着配置数据的开始。
偏移 0x464D:
偏移 0x5CFD:
注意:在执行流程传递给 vssapi.dll 之前,该函数会将字符串 Kraekgriesfid.xvs 复制到缓冲区的偏移 0x5E45 处,并将其转换为宽字符字符串。0x5E45 是从 Prangshound.hzj 读取的总大小。
您可以将从缓冲区偏移 0x464D 开始的 shellcode 保存到一个单独的文件,在 IDA 中打开该文件,然后跳转到文件偏移 0xED0 处查看反汇编后的入口点。
九、阶段 5:HijackLoader
在这个阶段,与上一阶段类似,它会动态解析许多 kernel32 和 ntdll 的 API,并调用 ZwQuerySystemInformation[28],使用 SystemProcessInformation 类将所有正在运行的进程枚举到 spi_buffer 中。它通过 NextEntryOffset 遍历生成的 SYSTEM_PROCESS_INFORMATION[29] 结构。对于每个有效的 ImageName 条目(例如 L"System"),它将进程名称复制到本地缓冲区,转换为小写,并通过一个辅助函数(此处标记为 mw_calc_hash)计算其哈希值。然后,将此哈希值与两个硬编码的值进行比较:
- •
0x6CEA4537→avgsvc.exe - •
0x5C7024B2→avastsvc.exe
如果任一比较匹配,它会设置 process_match_found 标志,并通过 NtDelayExecution 延迟执行 45 秒。avgsvc.exe 和 avastsvc.exe 分别是负责 AVG Antivirus 和 Avast Antivirus 核心功能的进程。
快速搜索这些硬编码的哈希值,可以确定此样本为 HijackLoader。Vladyslav Bahlai 的 分析[30] 中包含了相同的值,证实了这一点。
进程枚举之后,它会从 Kraekgriesfid.xvs 读取第二个加密的有效负载并将其解密。Trellix[31] 的 Ryan Weil 和 Vladyslav Bahlai 已经发表了关于 HijackLoader 解密和功能的深入分析,因此本报告不再重复他们的工作。如果您对 HijackLoader 的更多细节感兴趣,强烈推荐阅读他们的文章。
为求完整,需要说明的是,HijackLoader 顾名思义,是一个用于部署额外有效负载的加载器。它还提供各种用于规避和持久化的模块,并且通常被观察到部署 LummaC2 窃密木马作为其最终有效负载。
十、结论
GitHub 仓库是开发者常用的软件下载来源。由于开发者通常掌握着关键权限,攻击者正积极寻找入侵其机器的途径。我们的研究表明,攻击者能够如何劫持公共仓库,分发伪装成合法安装程序的恶意软件,这对组织和个体用户都构成了重大威胁。GMO Ierae 建议用户从官方的 Releases 页面下载安装程序,并在与赞助商搜索广告互动时保持警惕。
十一、检测
11.1 YARA 规则
之前我们提到,如何将 bundle header-offset 和 signature 与其他标识符结合,使用 YARA 搜索相关样本。以下是其实践形式。该规则检查 bundle header-offset 字段是否非零,并确认 bundle signature:
rule MAL_Loader_WIN_1
{
meta:
description = "Generic rule to detect the single-file malicious installer"
author = "GMO Cybersecurity by Ierae, Inc"
strings:
// .NET single-file bundle marker (sfbm)
// 4 bytes (non-zero), 4 bytes (any), 32-byte fixed tail
$sfbm = { ?? ?? ?? ?? ?? ?? ?? ?? 8B 12 02 B9 6A 61 20 38 72 7B 93 02 14 D7 A0 32 13 F5 B9 E6 EF AE 33 18 EE 3B 2D CE 24 B3 6A AE }
$a1 = "No OpenCL platforms found" ascii wide
$a2 = "No OpenCL GPU devices found" ascii wide
$a3 = "Failed to create context" ascii wide
$a4 = "Failed to create command queue" ascii wide
$a5 = "Failed to create program" ascii wide
$a6 = "Failed to build program" ascii wide
$a7 = "Failed to create kernel" ascii wide
$a8 = "generate_key" ascii wide
condition:
uint16(0) == 0x5a4d and
(#sfbm > 0 and
for any i in (1..#sfbm):
( uint8(@sfbm[i]) > 0 and
uint8(@sfbm[i]+1) > 0 and
uint8(@sfbm[i]+2) > 0 and
uint8(@sfbm[i]+3) > 0 )) and
(all of ($a*))
}
十二、入侵指标 IoC
12.1 恶意提交commits(SHA-1)
- •
3b3e14cec9f2c7f9567bb1a50ece12d4eb337305 - •
629f3ab77b0c6840618029d39869d078f8a5a694 - •
636f5d478fa774635da5b25ecb842822ab444009 - •
747971b32010ff652a6bd698fb57ece5287b9234 - •
a48188b0d5bdc3e8728cb37619cc51f7392b086f - •
e24d78ebb3c7302cc6aa8e2231f847a53e1345f2
12.2 托管恶意安装程序的 URL
- •
hxxps[://]git-desktop[.]app/git - •
hxxps[://]gitpage[.]app/ - •
hxxps[://]git-desktop[.]it[.]com/git
12.3 恶意安装程序(SHA-256)
- •
ad07ffab86a42b4befaf7858318480a556a2e7c272604c3f1dcae0782339482e - •
e252bb114f5c2793fc6900d49d3c302fc9298f36447bbf242a00c10887c36d71 - •
ec89c0ffc755eafc61bbf3b9106e0d9d7cbfaa9e70fbe17d9e4fbb9a7d38be64 - •
ed1811c16a91648fe60f5ee7d69fe455d0a3855eebb2f3d56909b7912de172fd - •
efcf5fe467f0ba8f990bcdfc063290b2cf3e8590455e6c7c8fe0f7373a339f36 - •
2a1c127683dba19399cc6516d5700d4e756933889dad156cd62b992aaf732816
12.4 解密后的 .NET 有效负载(SHA-256)
- •
e5c01a6f3d85c469e16857d92d9f0a1b01d14b0f0dad7df94b1afa6dc1ff4490 - •
731f03daacb38f70bf2178f2ab100b68fc189c9c8da19cc2be24d31d35e799b1
12.5 下一阶段 .NET 有效负载 URL
- •
hxxps[://]slepseetwork[.]online/api[.]php(observed at45.59.124[.]94:443) - •
hxxps[://]poiwerpolymersinc[.]online/api[.]php
12.6 PowerShell 加载器变体
| SHA-256 | 标记文件 | 计划任务 | 下一阶段有效负载 URL | 备注 |
| — | — | — | — | — |
| 8cd7d9ccea98ad6a3dfb4767e574349c9fd5678150c629661574ddd45e40cd37 | adm_marker.tmp | WinSvcUpd | hxxps[://]oqiwquwqey[.]xyz/zipep[.]php | |
| 6f9a1286f950da68e81bfe3e6c7655df00558df4d50289bf84df79c7d5073a2e | adm_marker.tmp | WinSvcUpd | hxxps[://]sleeposeirer[.]online/zip[.]php | 俄语注释 |
| 75deee7af25dc4f772661f17be4938c1980a703a785dc32274bf1647f8133cec | adm_marker.tmp | WinSvcUpd | hxxps[://]21ow[.]icu/arasa[.]php | |
| 2299b795169494d3717140bf34ea4574b6a9d7d8aecf77fd9ca932925373a23f | adm_marker.tmp | WinSvcUpd | hxxps[://]kololjrdtgted[.]click/zip[.]php | 俄语注释 |
| 95974060b0dfc45401d15ef9d07392b338fb7af2e3f623eb85b0ef5d1f5759d5 | adm_marker.tmp | WinCheckUpd | hxxps[://]lofiufueyer[.]blog/aps[.]php | |
| a46170be7cca7d8bcecf3da4caf035ec24f758eba45936ed802c1a03beab1c0a | adm_marker.tmp | WinSvcUpd | hxxps[://]polwique[.]blog/fils[.]php | |
| dbe1ec81fe1cb7f0249f47ed83be1b80ac99b2ae726a19b2083cb6fb585515d9 | admin.tmp | WinUpd , SystemUpdates | hxxps[://]21gweweqax[.]online/api[.]php | archive.zip 重命名为 app.zip |
| f3a914a46795021afd35b6c54a3c64ffedf33fbc3398dea84e6f71dc2d3ae198 | admin.tmp | SystemCheck , WinCheckupUpd | hxxps[://]appsiauer[.]online/api[.]php | archive.zip 重命名为 app.zip |
12.7 HijackLoader 入侵指标
| 文件 | SHA-256 | 备注 |
| — | — | — |
| Control-Binary32.exe (32-bit) | 79384ef76740962757d617bc056bf8a45b2ef8f1e1587632b36830e2fc6ab21a | 合法可执行文件(已签名)。 |
| Qt5Network.dll (32-bit) | 719a726d54161a1a95cf69f3001b74fe15661b83d995b89bcca5ecc8e792e2eb | 被劫持的 DLL;加载 Prangshound.hzj。 |
| FileAssocation.dll (32-bit) | 95d51ee9c58f789213cedac7e82c7ba064364d9e5c8ca76ad27a5e53537f9fdf | 合法依赖 DLL(已签名)。 |
| Qt5Core.dll (32-bit) | b967ade09a933832e0db4e5da11a2ac396950f0eed689b28bd31686b7baf018 | 合法依赖 DLL(已签名)。 |
| Prangshound.hzj | 58f897d4369a4c667b2f40a6703c7ae42912a10186d81c6eaa7809513da86a51 | 加密的部署数据(用于模块替换的模块、明文密钥、配置)。 |
| Kraekgriesfid.xvs | be503f616edacac10689b63ba39c4b5d791fcf365bc80a0c8bc27c2c3d3cb2a4 | 加密的 HijackLoader 模块/配置 + 加密的最终有效负载。 |
其他参考资料
- • Cédric Luthi 关于单文件 .NET 应用程序的研究[32]
- •
clGetDeviceInfo[20] /clGetPlatformInfo[21] 的使用示例 - • (查询
CL_DEVICE_NAME和CL_PLATFORM_NAME[33])
引用链接
[1] 《Revisiting GPUGate: Repo Squatting and OpenCL Deception to Deliver HijackLoader》: https://gmo-cybersecurity.com/blog/revisiting-gpugate-repo-squatting-and-opencl-deception-to-deliver-hijackloader/
[2] 报告: https://gmo-cybersecurity.com/blog/phantom-commit-injection/
[3] GitHub Desktop: https://github.com/desktop/desktop
[4] “GPUGate”: https://arcticwolf.com/resources/blog/gpugate-malware-malicious-github-desktop-implants-use-hardware-specific-decryption-abuse-google-ads-target-western-europe/
[5] OpenCL: https://www.khronos.org/opencl/
[6] GitHub 官方文档: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-permissions-and-visibility-of-forks#important-security-considerations
[7] bundler: https://github.com/dotnet/designs/blob/main/accepted/2020/single-file/design.md
[8] “将托管应用程序及其依赖项作为二进制数据块附加在 AppHost 可执行文件的末尾。”: https://github.com/dotnet/designs/blob/main/accepted/2020/single-file/bundler.md
[9] bundle marker: https://github.com/dotnet/runtime/blob/v6.0.3/src/native/corehost/apphost/bundle_marker.cpp#L19-L23
[10] OpenCL 可安装客户端驱动程序 (ICD) 加载器: https://github.com/KhronosGroup/OpenCL-ICD-Loader
[11] 研究: https://eversinc33.com/2023/03/18/abusing-the-gpu-for-malware-with-opencl
[12] clCreateProgramWithSource: https://registry.khronos.org/OpenCL/sdk/3.0/docs/man/html/clCreateProgramWithSource.html
[13] clBuildProgram: https://registry.khronos.org/OpenCL/sdk/3.0/docs/man/html/clBuildProgram.html
[14] clCreateKernel: https://registry.khronos.org/OpenCL/sdk/3.0/docs/man/html/clCreateKernel.html
[15] clSetKernelArg: https://registry.khronos.org/OpenCL/sdk/3.0/docs/man/html/clSetKernelArg.html
[16] clEnqueueNDRangeKernel: https://registry.khronos.org/OpenCL/sdk/3.0/docs/man/html/clEnqueueNDRangeKernel.html
[17] clEnqueueReadBuffer: https://registry.khronos.org/OpenCL/sdk/3.0/docs/man/html/clEnqueueReadBuffer.html
[18] clGetPlatformIDs: https://registry.khronos.org/OpenCL/sdk/3.0/docs/man/html/clGetPlatformIDs.html
[19] clGetDeviceIDs: https://registry.khronos.org/OpenCL/sdk/3.0/docs/man/html/clGetDeviceIDs.html
[20] clGetDeviceInfo: https://registry.khronos.org/OpenCL/sdk/3.0/docs/man/html/clGetDeviceInfo.html
[21] clGetPlatformInfo: https://registry.khronos.org/OpenCL/sdk/3.0/docs/man/html/clGetPlatformInfo.html
[22] GetByteArrayAsync: https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.getbytearrayasync?view=net-10.0
[23] 样本: https://any.run/report/0386f0c592e9ad8a054482918feaaa7cd7edcc65c5c3d7c7f62ecdda7e3c9fb1/1aa88ae6-89cc-4ee1-b7d6-b1234d2331cf
[24] Elastic Security Labs: https://www.elastic.co/security-labs/a-wretch-client
[25] Control-Binary32.exe: https://www.virustotal.com/gui/file/79384ef76740962757d617bc056bf8a45b2ef8f1e1587632b36830e2fc6ab21a/detection
[26] FileAssocation.dll: https://www.virustotal.com/gui/file/95d51ee9c58f789213cedac7e82c7ba064364d9e5c8ca76ad27a5e53537f9fdf
[27] Qt5Core.dll: https://www.virustotal.com/gui/file/b967ade09a9338320e0db4e5da11a2ac396950f0eed689b28bd31686b7baf018/details
[28] ZwQuerySystemInformation: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/query.htm
[29] SYSTEM\_PROCESS\_INFORMATION: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/process.htm?tx=60
[30] 分析: https://medium.com/@baglai.vlad/hijackloader-ghostpulse-idat-loader-comprehensive-analysis-6e15f48eb96d
[31] Trellix: https://www.trellix.com/blogs/research/analysis-of-hijackloader-and-its-infection-chain/
[32] Cédric Luthi 关于单文件 .NET 应用程序的研究: https://github.com/0xced/SingleFileAppDependencyContext?tab=readme-ov-file
[33] 查询 CL\_DEVICE\_NAME 和 CL\_PLATFORM\_NAME: https://gist.github.com/tserj/ed03e874d288be2a485089feed5ac08f
交流群
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:云原生安全指北 Dubito
Dubito《仓库抢注:Docker Desktop官方仓库成恶意软件分发渠道》