-
-
[原创]一例银狐恶意样本深度分析:高度隐蔽、多层架构与无文件落地
-
发表于: 15小时前 72
-
近日,瞬犀安全的安全团队捕捉到一个银狐样本,该样本是一个具备极高隐蔽性、采用多级架构且完全无文件落地的复杂恶意软件。该威胁专注于深度防御规避与安全产品致盲,打破传统安全产品的遥测行为链。
样本利用了前沿的进程注入技术(如 IOCP 线程池注入 PoolParty 变种 7 结合句柄劫持),并通过替换 NTDLL 代码段和加载漏洞驱动(BYOVD)来从用户态和内核态双向瘫痪安全监控。其载荷投递采用多级反射式注入,全程在内存中解密与执行(双重压缩与加密的 Shellcode),并利用白进程 DLL 侧加载和 COM 劫持实现高隐蔽持久化。此外,该恶意软件集成 libwebsockets网络通信库,支持多协议(HTTP/TCP/UDP)加密通信,在与C2服务器建立连接后,直接在内存中创建线程运行下发的 Shellcode,全程无文件落地,传统的基于文件特征的杀毒软件对这种攻击几乎完全失效,这也是APT最常用的免杀手段。
危害分析
该恶意软件对企业网络构成了严重的系统性风险,具体危害体现在以下三个核心层面:
1. 全面瘫痪端点防御(EDR致盲)
o 用户态防御失效: 通过动态 API 解析和 NTDLL 代码段替换(Unhooking),能够绕过绝大多数基于用户态 API 挂钩的 EDR 行为监控。
o 内核态安全受损: 利用“自带漏洞驱动”(BYOVD)技术,恶意软件可以直接干预内核,关闭或削弱安全软件的驱动程序,让目标主机处于完全不设防的“致盲”状态。
2. 极高的隐蔽性与长效持久化
o 阻断溯源分析: 强烈的“单进程行为链聚合规避”意识和 PoolParty 注入技术,使得恶意行为被打散到正常的系统线程池中,让传统的基于行为链关联的威胁检测(XDR/EDR)失效。
o 白文件驻留: 结合 COM 劫持、白名单进程和 DLL 侧加载,恶意软件与拥有微软签名的进程深度绑定。每次系统启动时,恶意载荷均以合法身份在内存中动态组装,硬盘上无任何直接的恶意可执行文件落地。
3. 无文件落地的下发载荷执行
o 高性能 C2 通道: 支持 HTTP、TCP、UDP 多协议且配置与载荷双重加密,集成 libwebsockets 高性能网络通信库。
o 载荷内存执行: C2 下发的载荷直接在内存中执行,全程无文件落地。
流程概括
恶意行为深度逆向
Injector
字符串加密
该恶意程序的所有字符串都不会以明文的方式暴露在文件中,防止被 strings, floss 这类提取工具扫描到。该恶意软件采用了基于 SIMD(SSE2)指令集的混淆字符串方式,它不再使用简单的单字节运算,而是利用了处理器的 128位寄存器 (XMM),通过并行、位移、异或和重排 (Shuffle) 操作还原成字符串(例如 API 函数名)。
恶意软件作者通过引入一个全局变量 dword_14001A004,打破了纯函数的特性。由于这是一个内存地址,静态分析工具无法确定这个值在运行时是否会被其他线程或前期初始化的代码修改。因此,反编译器无法通过编译优化算法去化简这部分代码或自动算出明文内容。
在编译器优化理论中,如果一个函数只依赖于输入参数,且没有副作用,它被称为纯函数 (Pure Function)。一般反编译器都具有常量折叠和数据流分析功能。如果解密函数是纯函数,且输入参数是固定的(比如本例无参数),反编译器能在分析时直接计算出结果。
使用 unicorn 来模拟执行这些函数,核心代码如下:

使用函数引用的地址 0x14001A004 作为种子,扫描交叉引用,对这些函数进行 unicorn 模拟。最终得到的字符串列表如下:
提权与特权升级
该恶意软件通过动态库加载与 API 调用,确保恶意进程在具备管理员权限的环境下运行,以解除后续操作系统级操作的权限限制。校验当前进程令牌的完整性级别。若校验结果返回为 0(即非管理员权限),控制流将强制跳转至 restart_as_admin 函数,并立即调用 ExitProcess(0) 终止非特权进程,将控制权移交给后续启动的新实例。
restart_as_admin 函数实现了基于 UAC 机制的特权升级,调用 decrypt_str_runas 解密“runas”,并将其赋值给 lpVerb 成员。这是调用 Windows 提权机制的关键参数。该流程将恶意代码自身的绝对路径(通过 GetModuleFileNameW 获取)传入 ShellExecuteExW 函数。随着新进程以管理员权限启动,原始非特权进程随即退出,完成特权环境的切换。
注入目标选择
该恶意软件在执行核心的线程池劫持(PoolParty)注入之前,通过探测宿主机上运行的特定终端防御产品(EDR/AV),动态决定注入的系统级服务宿主。
程序遍历当前系统的进程列表,进行硬编码特征匹配。恶意软件未采用高危的进程句柄请求,仅依赖于只读的进程快照读取,从而降低了在环境探测阶段被杀毒软件主动防御拦截的概率。
360tray.exe 探测
bdservicehost.exe(BitDefender 核心服务)探测
当检测到上述任意一款安全软件处于运行状态时,恶意软件将注入目标锁定为 Volume Shadow Copy (VSS) 服务。若该服务未运行,恶意软件会调用内部函数 start_vss_service 强制启动,并进入一个带有 Sleep 延迟的轮询循环,直到成功获取 VSS 服务的 PID。
若 PID 检索返回为 0,意味着目标 VSS 服务当前处于停止状态。恶意软件将立刻调用 start_vss_service 执行强制提态与激活。函数通过 OpenSCManagerW(0, 0, 2) 获取服务控制管理器的句柄,随后调用 OpenServiceW 显式请求对 VSS 服务的控制权(请求权限掩码为 SERVICE_ALL_ACCESS)。成功获取服务句柄后,调用 StartServiceW 强行更改服务状态机,迫使系统派生合法的 vssvc.exe 进程。
在触发 StartServiceW 之后,当前进程为了规避单进程行为链聚合,再次实施了进程切断。在成功启动了一个全新的自身实例之后,立即结束自己。
这种设计的核心对抗目标并非人工溯源审计(父子进程树在 Sysmon 事件 ID 1 中依然可见),而是旨在精准扰乱现代 EDR(尤其是依赖 ETW-TI 遥测数据的受保护进程 PPL)的自动化上下文聚合与风险评分机制。它将“触发高权限系统服务启动(SCM 交互)”与“执行后续跨进程注入(句柄窃取与内存操作)”这两个连续的高危行为,在系统底层强行拆分至两个完全独立的进程上下文(EPROCESS)中执行。
· 原实例(PID A): 仅执行了常规的服务拉起与子进程派生,其单一进程的行为风险评分远低于 EDR 的启发式拦截阈值。
· 新实例(PID B): 在第二次运行时,由于 VSS 服务已被前置激活,它将直接走通 PID 检索流程,直接获取合法 vssvc.exe 的句柄并执行 PoolParty 劫持。
虽然高级 EDR 具备追踪进程树的能力,但在实际内核级监控中,跨进程生命周期的细粒度行为意图聚合(即将 PID A 的服务状态操控与 PID B 的底层句柄窃取进行实时强关联分析)具有极高的计算开销,且极易引发正常业务系统的误报。攻击者正是利用了 EDR 引擎在“状态机重置”与“性能开销平衡”上的妥协,通过主动切断单一进程的行为时间线,迫使 EDR 遥测数据碎片化,从而规避了单进程行为链聚合所触发的实时拦截。
若探测确认当前环境不存在上述指定的安全软件,恶意软件将执行降级策略,将目标切换至 Task Scheduler (Schedule) 服务。
Shellcode 解密
该恶意软件将加密后的Shellcode嵌入在程序自身,Shellcode采用了双重压缩与加密的方式。该区域的结构如下:
偏移 | 大小 | 意义 |
0x00 | 0x01 | 垃圾数据的长度,该值为 0xA8 |
0x01 | 0xA8 | 垃圾数据 |
0xA9 | 0x04 | 压缩数据的长度,该值为 0xB13 |
0xAD | 0xB13 | 压缩数据 |
恶意软件使用标准的 LZNT1 压缩算法,并自己实现了LZNT1解压算法。解压后的数据结构如下:
偏移 | 大小 | 意义 |
0x00 | 0x01 | 密钥的长度,该值为 0x56 |
0x01 | 0x56 | 密钥内容 |
0x57 | 0x04 | 压缩数据的长度,该值为 0x97B |
0x5B | 0x97B | 压缩数据 |
恶意软件使用密钥解密之后,得到的数据仍然是 LZNT1 压缩数据,随后恶意软件再次调用自己实现的LZNT1解压算法得到Shellcode。
NTDLL 内存映像恢复
恶意软件在进行NTDLL脱钩之前会将自己提升至调试权限,以便后续对高权限进程进行内存扫描并执行无线程注入。
恶意软件通过从磁盘加载一份未受污染的 ntdll.dll 干净映像,并强行覆盖当前进程内存中已被 EDR 挂钩的代码段(.text),从而彻底清除 EDR 在用户态部署的所有监控点。
程序调用 GetSystemDirectoryW 获取系统目录(通常为 C:\Windows\System32),随后解密出字符串 \ntdll.dll 并进行拼接,确保精确定位到合法的系统核心动态链接库。
由于 MapViewOfFile 配合 SEC_IMAGE 已经将文件自动展开为内存对齐的 PE 结构,恶意软件开始手动解析当前进程中已加载的 ntdll.dll(由 ProcessHandle 指向其基地址)的 PE 头,以精确定位代码段。程序解密出字符串 .text,并以双 DWORD(8 字节)的宽度逐个比对 Section 名称。一旦匹配成功,说明已成功定位到原始机器码的代码段。
在定位到 .text 段后,恶意软件执行强制覆盖,借助 memcpy 程序将刚才从磁盘映射出来的、未经 EDR 注入挂钩的干净 .text 代码段数据(由 v118 指向),强行覆盖到当前进程正在使用的 ntdll.dll 内存区间。覆写完成后,恶意软件再次调用 VirtualProtect 将该区间恢复为原始保护属性。最后,调用 UnmapViewOfFile 清理磁盘映射缓冲区。
内存扫描器
该恶意软件内部实现的 find_pattern_in_memory 函数是一个高度优化、具备硬件加速能力的远程进程内存特征码扫描引擎。在整体攻击链中,该模块负责在目标进程的虚拟地址空间内,检索前置投递的 Shellcode 和 _TP_DIRECT,这样即可让子进程定位父进程在目标进程中写入的 Shellcode 或其他数据。
恶意软件根据特征码长度(PatternSize),设计了三级启发式硬件加速匹配
· 当检索特征长度大于 16 字节时,启用 x64 架构的SSE2 向量化指令集。一次性从内存中并发加载 128 位(16 字节)数据进行比对。
· 特征码长度处于中等区间(4
PatternSize
17 字节),采用双字(4字节)为对齐基准对比
· 逐字节校验(PatternSize
4 字节)
无线程注入
控制流劫持结构初始化
恶意软件在目标进程中部署 Shellcode 以及伪造线程池任务控制块之前,会先通过内存扫描来检测是否存在上一个进程已经注入的 Shellcode 和 _TP_DIRECT ,这正是开发者为了规避单进程行为链聚合。
如果未搜寻到内存特征就手动部署 Shellcode 和伪造线程池控制块。并且在部署之前,恶意软件已经进行了NTDLL脱钩,该过程通过调用已经脱钩的 Native API 以规避用户层挂钩。
为了实现无线程注入,恶意软件通过构造 _TP_DIRECT 来接管控制流。将 Shellcode 的远程地址写入了 _TP_DIRECT 的 Callback 字段中。恶意软件会直接调用 NtSetIoCompletion,将这个结构体的指针作为“完成键(Completion Key)”或“重叠结构(Overlapped)”强行推入目标进程的 IOCP 中。目标进程中处于睡眠状态的 TppWorkerThread 被唤醒。工作线程从 IOCP 中取出一个数据包,发现它是一个直接调用(Direct Task)。操作系统此时不进行任何额外的安全或上下文校验,直接将指针强转为 PTP_DIRECT,读取偏移 0x38 处的 Callback 地址,并立即执行。
typedef struct _TP_TASK
{
struct _TP_TASK_CALLBACKS* Callbacks;
UINT32 NumaNode;
UINT8 IdealProcessor;
char Padding_242[3];
struct _LIST_ENTRY ListEntry;
} TP_TASK, * PTP_TASK;
typedef struct _TP_DIRECT
{
struct _TP_TASK Task;
UINT64 Lock;
struct _LIST_ENTRY IoCompletionInformationList;
void* Callback;
UINT32 NumaNode;
UINT8 IdealProcessor;
char __PADDING__[3];
} TP_DIRECT, * PTP_DIRECT;
这 0x48 字节的数据内容如下:
旁注:Windows 线程池 TP_DIRECT 机制与安全盲区
机制概述 在 Windows 操作系统的线程池(Thread Pool)架构中,系统通过不同类型的结构体来管理异步任务。常规任务(如 TP_WORK)会被投递至线程池内部的任务队列中,由调度器进行复杂的环境与状态校验后方可执行。而 _TP_DIRECT 是一种高度优化的“直接执行”结构。它不经过常规的线程池队列排队,而是被直接推入底层的 I/O 完成端口(IOCP)。
信任快速通道与滥用原理 当底层工作线程(Worker Thread)从 IOCP 唤醒并取出 _TP_DIRECT 数据包时,为了追求极致的异步 I/O 处理性能,系统调度器会跳过针对常规工作项的严格上下文验证,直接提取结构体偏移 0x38 处的 Callback(回调函数)指针并跳转执行。
攻击者(采用 PoolParty Variant 7 技术的恶意软件)正是利用了操作系统的这一“信任快速通道”。攻击者仅需在目标系统服务中伪造一个 72 字节(0x48)的 _TP_DIRECT 结构体,将恶意载荷地址覆盖至回调函数指针处。随后通过合法的 IOCP 通知触发执行,即可在无需调用任何高危“线程创建” API 的前提下,实现高权限的静默代码执行。这种利用底层调度机制合法性的攻击,使得传统的行为监控难以进行有效拦截。
在执行完 PoolParty 注入后,代码跳到了 LABEL_164。这里的目的是启动自身的一个新实例。
IOCP劫持
如果前一个进程(比如父进程)已经在目标进程的中部署了 Shellcode和 _TP_DIRECT 结构体,恶意软件会搜寻 Shellcode 的内存地址,并重新构造完全相同的 _TP_DIRECT 结构体作为内存特征,调用内存扫描函数定位该结构体的基地址。
恶意软件首先进行一次系统级的句柄快照,调用 DuplicateHandle 将目标进程的句柄复制到自身。
对于每一个复制到自身的句柄,恶意软件会进行一次对象查询,直到找出 IoCompletion 对象。
在完成载荷投递、_TP_DIRECT 伪造和句柄窃取之后,恶意软件进入执行链的最终环节。
恶意软件通过调用 PostQueuedCompletionStatus(或在 API 解析失败时,降级调用未文档化的 Native API ZwSetIoCompletion)向目标进程的 IOCP 队列中手动插入一个完成包。 在调用该 API 时,恶意软件将预先写入目标进程内存的 _TP_DIRECT 作为 dwCompletionKey 或 lpOverlapped 参数传入。操作系统内核接收到该请求后,将其视为一次合法的异步 I/O 完成事件,并将包含该结构体地址的数据包压入目标进程 IoCompletion 的先进先出(FIFO)队列中。
Windows 线程池引擎在处理此类直接任务(Direct Task)时,为保证极高的调度性能,忽略了对任务来源与合法性的深度校验。调度逻辑将该指针强制转换为 _TP_DIRECT 结构体指针。工作线程直接读取该结构体偏移 0x38 处的 Callback 成员变量。由于该位置已被恶意软件覆写为 Shellcode 的物理内存基址,工作线程随后执行函数指针调用,跳转至预先写入的 Shellcode。
挂起与自毁
在程序遇到未预料到的异常或者注入已经执行完毕,程序的控制流会进入这里。该恶意软件会拍摄一次进程快照,一旦检测出 bdservicehost.exe,程序随即触发 Sleep(1000u)(挂起 1 秒),并重新执行快照拍摄与检索逻辑。
如果当前系统运行的程序中不存在 bdservicehost.exe 或者该进程已经被关闭。恶意软件开始执行自毁。
恶意软件首先通过 GetModuleFileNameW 获取当前执行文件的绝对路径,并获取 %TEMP% 目录。随后利用 GetTempFileNameW 生成一个临时的、随机命名的文件路径。程序调用 MoveFileW,将当前正在运行的、位于显眼路径下的原始可执行文件,移动到 %TEMP% 目录下的临时占位文件中。由于 Windows 文件系统允许在同一逻辑分区内对打开了执行句柄的文件进行重命名或目录项迁移,该操作可以合法执行。移动完成后,恶意软件调用了 MoveFileExW 并传入参数 4u,对应 Windows 内核常量 MOVEFILE_DELAY_UNTIL_REBOOT。执行该调用时,系统并不会立刻删除文件,而是向注册表的以下路径写入一个删除标记项: HKLM\System\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations
最终,程序调用 ExitProcess(0) 终止当前进程。在系统下一次重启时,内核的会话管理器进程(smss.exe)会在配置子系统初始化之前、且在任何用户态程序启动之前,优先读取该注册表项,由内核将 %TEMP% 目录下的临时文件彻底抹除。
Shellcode:下载器
该Shellcode的布局如下:
偏移 | 大小 | 意义 |
0x00 | 0xA9C | Shellcode代码 |
0xA9C | 0x8 | RC4初始秘钥 |
0xAA4 | 0x4 | 密文长度,该值为 0x15B |
0xAA8 | 0x15B | C2配置项密文内容 |
Shellcode通过内置的HASH,动态计算加载到内存中的动态链接库的导出表的导出函数的HASH,如果HASH比对成功,就返回该函数的地址。
Shellcode通过搜索第一个字节是 0xFE,第二个字节是 0xFA,第八个字节是 0xFF 来确定这八个字节就是RC4秘钥。
随后 Shellcode 使用该秘钥解密密文
解密后的密文仍然是一段压缩后的数据,Shellcode 通过调用 RtlDecompressBuffer 来执行内存中解压缩。
随后 Shellcode 根据解密出来的配置项,向远程服务器请求下一阶段的载荷,Shellcode 将服务器返回加密后的载荷拷贝到刚刚申请的内存中。跳过自定义头部,加密并备份C2配置项,创建新线程执行负责解密的 Shellcode。
在加密过程中,该 Shellcode选择了新的秘钥执行加密。
最终加密的C2配置会被存放在服务器响应数据中预留的一段空区域,这块数据的头部已经预填充好了SIZE(0xA88)。
Shellcode:解密器
C2服务器返回的数据布局如下:
偏移 | 大小 | 意义 |
0x00 | 0x32 | 自定义头部 |
0x32 | 0x4 | Shellcode(解密器)的大小,该值为0xE70 |
0x36 | 0xE70 | Shellcode(解密器) |
0xEA6 | 0x4 | 反射DLL(包含加密秘钥)的大小 |
0xEAA | 0x1 | RC4秘钥的大小,该值为0xDF |
0xEAB | 0xDF | RC4秘钥 |
0xF8A | 0x2DC76 | 加密的反射DLL |
在解密器被启动时,会接收一个参数,这个参数指向了服务器返回的数据+0x32偏移处。也就是指向 Shellcode(解密器)的大小(如表格所示)。
反射DLL仍然使用RC4加密
恶意软件作者故意将前 60 个字节(0x00 到 0x3B)用 00 覆盖。这种典型的 Header Stomping(头部踩踏) 技术,旨在抹除 IMAGE_DOS_HEADER(包含关键的 MZ 标志和 e_lfanew 指针)以及 IMAGE_NT_HEADERS 的签名(PE\0\0),以此来瘫痪大部分依赖标准 PE 结构的内存扫描工具(如 Moneta、PE-sieve 或常规的 EDR 内存扫描)。
通过对残缺部分的偏移对齐,我们可以直接读取出 IMAGE_FILE_HEADER 及后续的关键数据。恶意软件作者仅仅抹除了前 60 字节,从第 60 字节(偏移 0x3C)开始,所有数据都是原生的:
· Machine (架构): 64 86 (AMD64 / x64)
· NumberOfSections (区段数): 05 00 (共 5 个区段)
· TimeDateStamp (时间戳): 00 57 F0 69
· SizeOfOptionalHeader (可选头大小): F0 00 (0xF0,标准的 64 位可选头大小)
· Characteristics (属性): 22 20 (DLL、支持大地址、可执行)
· AddressOfEntryPoint (入口点): 38 44 00 00 (OEP 位于 0x4438)
· ImageBase (内存基址): 00 00 00 80 01 00 00 00 (0x180000000)
· 区段表 (Section Table): 保留了 .text, .rdata, .data, .pdata, .reloc。
从 .text 区段的定义 04 9C 00 00 00 10 00 00 00 9E 00 00 00 04 00 00 可以看出,它的 PointerToRawData(文件物理偏移)是 0x0400。这意味着,真正的头部区块 (SizeOfHeaders) 总共占据 0x400 个字节。
Implant:环境探测与持久化
该恶意软件会在启动时先拷贝重新加密后的C2配置内存,接着解密配置等待后续的使用。
该恶意软件同样会在当前进程中重新申请一块可执行内存,将 Shellcode解密器 写入并创建新线程执行。
在创建新线程的时候传入加密的C2 Implant的内存地址,Shellcode Loader会在内存中解密这块内存,并使用反射加载技术在当前进程中加载这个DLL(详情见上文)。
解密后的内容如下:
当检测到 bdservicehost.exe 运行时,恶意软件会释放并加载一个驱动。恶意软件释放的驱动是以明文的方式存在于内存中,不过安全目录会被破坏。在释放的时候会先在内存中修复安全目录接着重命名为随机文件名释放到 system32\\dirvers\\ 目录中。安全目录被修复后,驱动的签名信息已经正常,Windows根据驱动签名认定这是一个合法的驱动,从而顺利加载。
L"C:\Windows\system32\drivers\tp_88329343.tmp"
在加载驱动之后,使用控制码结束安全进程。
对应的驱动代码如下:
恶意软件结合 COM 劫持、白名单进程和 DLL 侧加载实现持久化。installer.exe 作为白进程,拥有合法的签名,log.dll 是恶意的DLL,在白进程启动的时候加载到白进程中并读取磁盘上的 log.bin 文件解密注入到系统进程中 Schedule / VSS 等服务。log.bin 是加密的 C2 Implant,通过反射式注入加载到宿主进程并与C2服务器进行通信。
恶意软件通过 ITaskFolder::RegisterTaskDefinition 接口将其写入系统,指定以 SYSTEM 身份运行。
C2 Implant
C2 Implant的检测逻辑如下:
恶意软件首先初始化加密的配置,随后在当前的宿主进程中执行NTDLL脱钩,抹除安全软件在用户态的 API 钩子。
随后该恶意软件修改 NtTraceEvent 函数,使其直接返回,切断普通安全产品的遥测日志来源。
情景1:由持久化进程作为起始点
恶意软件首先会检查当前进程是否是 svchost.exe (Schedule或者后续的任一 svchost.exe 傀儡进程)。如果当前的 C2 Implant 存在于 installer.exe 中,该恶意软件会尝试获取父进程的PID,并确保父进程是 Schedule 或 services.exe 两者之一,如果都不是就结束父进程,随后尝试获取 installer.exe 进程的PID,并且结束它。installer.exe 只能被这两者拉起来,否则就是一次异常的运行。这是恶意软件作者的反分析机制,此举会直接终结分析人员的调试器。
如果恶意软件的父进程是 Schedule 或 services.exe 任一一个,则恶意软件判断这是一次由持久化服务拉起的。此时恶意软件尝试去获取 Schedule 的PID,如果未获取到就拉起一个 svchost.exe 注入。
如果恶意软件成功获取了 Schedule 的PID,就使用创建远程线程的方式进行注入。
如果Windows配置了 SYSMON,那么日志中会详细地记录此次注入攻击:
{
"EventData": {
"Data": [
{ "@Name": "RuleName", "#text": "CreateRemoteThread_Injection" },
{ "@Name": "UtcTime", "#text": "xxxx-xx-xx xx:xx:xx.xxx" },
{
"@Name": "SourceProcessGuid",
"#text": "2eca7886-780d-6a4b-2500-000000001100"
},
{ "@Name": "SourceProcessId", "#text": "1828" },
{
"@Name": "SourceImage",
"#text": "C:\\Program Files\\Internet Explorer\\installer.exe"
},
{
"@Name": "TargetProcessGuid",
"#text": "2eca7886-780d-6a4b-1b00-000000001100"
},
{ "@Name": "TargetProcessId", "#text": "1328" },
{ "@Name": "TargetImage", "#text": "C:\\Windows\\System32\\svchost.exe" },
{ "@Name": "NewThreadId", "#text": "5540" },
{ "@Name": "StartAddress", "#text": "0x00000212D5BB0000" },
{ "@Name": "StartModule", "#text": "-" },
{ "@Name": "StartFunction", "#text": "-" },
{ "@Name": "SourceUser", "#text": "NT AUTHORITY\\SYSTEM" },
{ "@Name": "TargetUser", "#text": "NT AUTHORITY\\SYSTEM" }
]
}
}
情景2:由注入器作为起始点
如果当前的宿主进程是 vssvc.exe,这代表本次的启动是由注入器注入到 VSS 服务后拉起的。如果宿主进程是 svchost.exe,但是该进程是 Schedule 服务,恶意软件也会执行下面的代码。
恶意软件会再次拉起一个 svchost.exe,同时将 C2 Implant 注入其中。
对于 svchost.exe 的注入,恶意软件采用了线程劫持的方式。
netsvcs 是 Windows 系统中最常见、最繁忙的共享服务宿主进程之一(负责网络连接、Windows Update 等大量关键服务)。攻击者将其作为注入目标,一是因为它通常以 SYSTEM 权限运行,二是因为它本身就会产生大量的网络通信,恶意软件的 C2 流量混杂其中极难被安全运维人员察觉(极佳的“白加黑”掩护)。
C2 上线
当上述判断逻辑都没有命中,恶意软件知道自己存在于 svchost.exe -k netsvcs 进程中。此时开始与 C2 服务器建立连接。
恶意软件内置了三种通讯协议(UDP,TCP,HTTP,由C2配置决定)用来与服务器通信。
该恶意软件的网络通信底层采用了开源的 libwebsockets (LWS) 库。这是一个轻量级、纯 C 语言编写的异步网络通信引擎。
与传统的“每个连接分配一个线程”的阻塞式 Socket 编程不同,libwebsockets 采用了单线程事件轮询(Event Loop)架构。引擎内部维护着一个复杂的状态机(State Machine),程序不会在等待网络数据时卡死,而是当特定的网络事件(如连接成功、数据到达、连接断开)真实发生时,底层才会触发通知。LWS 库并不负责处理具体的业务逻辑,而是将所有网络事件的处置权交给了开发者注册的回调函数 (Callback)。
回调函数内部中存在下面这段代码。恶意软件一直在后台等待该信号。一旦被唤醒,他便解析服务器下发的数据,将其作为Shellcode,在内部创建线程执行。
这种执行方式完全没有文件落地。C2 下发的恶意载荷只存在于内存中运行。传统的基于文件特征的杀毒软件对这种攻击几乎完全失效,这也是APT最常用的免杀手段。
IoCs
MD5:487f367bb135f666ee4d0fedc22d896b
SHA-1:f26e5f1e5d9b04355e88bd846d0e53134fb79d17
SHA-256:2c0f9afe957edc04d93f2590dcc5a1a337e41a0ceea5b2817b0e204c00c160d6
[内核课程]《Windows内核攻防实战》!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。