研究人员近期发现了一种高对抗强度的 Loader,其通过钓鱼邮件附件传递给受害者。根据恶意软件所具备的引诱和规避行为,研究人员将其命名为 SquidLoader。SquidLoader 最早在 2024 年 4 月下旬被发现,但研究人员认为其至少已经活跃了一个月以上。
SquidLoader 后续投递的是 Cobalt Strike,也都经过加强以对抗静态分析。根据 SquidLoader 的配置信息,研究人员发现过去两年中主要在针对讲中文的受害者进行攻击。攻击者的攻击技术与攻击策略有可能会被他人模仿,在不久的将来被其他攻击者应用到攻击其他受害者。
Loader 分析
2024 年 4 月下旬,研究人员观察到一些特别的钓鱼邮件附件,文件名为“华为工业级路由器相关产品介绍和优秀客户案例”。顺藤摸瓜发现一系列以中国企业/组织名称命名的恶意附件,如“中国移动集团XX分公司”、“嘉X智能科技”与“XX水利技术学院”。所有样本都使用 Word 文档的图标,吸引受害者打开这些文档文件,但实际上执行的是二进制文件。
这些 Loader 具备强大的引诱和规避能力,帮助恶意样本保持不被发现,也阻止分析人员进行分析。下载的 Shellcode 也加载进同一 Loader 中,不写入磁盘也降低了被发现的风险。
发现的大多数样本文件都使用合法的过期证书,使样本文件看起来很正常。使用证书之一是颁发给杭州瑛格科技有限公司的,在 2021 年 7 月 15 日到期。证书的指纹为 3F984B8706702DB13F26AE73BD4C591C5936344F,序列号为 02 0E B5 27 BA C0 10 99 59 3E 2E A9 02 E3 97 CB。当然,这并不是唯一的无效证书。
SquidLoader 使用的 C&C 服务器采用自签名证书,其签发者和主体都包含:
- Common Name: localhost
- Organizational Unit: group
- Organization: Company
- Locality: Nanjing
- State/Province: Jiangsu
- Country: CN
首次执行时,SquidLoader 会复制到特定位置(C:\BakFiles\install.exe),再从新位置重新启动。这可能是攻击者故意的,想通过不可疑的名称执行,因为 SquidLoader 并不具备任何持久化机制。只是后续投递的 Cobalt Strike 能够创建服务并修改注册表,可以实现攻击者的持久化目标。
Shellcode 通过 5 字节的异或密钥进行加密,密钥使用小端存储硬编码为 DE FF CC 8F 9A。
异或解码
尽管文件名和图片都模拟 Word 文档文件,但样本文件中包含大量引用微信或 mingw-gcc 等软件的代码,以此来误导研究人员。文件的元数据中也是如此,想让受害者相信恶意文件只是这些软件的合法组件。当然,这些代码永远不会被执行。
其他代码
所有观察到的可执行文件都有类似于 Microsoft Office Word 的文档图标,样本文件还会弹出中文告警信息:
告警信息
检测规避技术
SquidLoader 使用了大量的检测规避技术,此处简单列举几个:
使用无意义/模糊的指令
文件中包含模糊且无意义的 x86 指令,例如 pause、mfence 或 lfence。某些函数还包含填充指令,例如根本不使用的随机算术计算。这可能是攻击者想要破坏或者绕过反病毒模拟器的尝试,这些检测手段可能并未实现不太常见的指令,或者可能只能运行模拟指令。
加密代码段
恶意软件开始执行后立即加载内嵌的加密 Shellcode,在动态分配的内存段中进行解密,赋予该段执行权限并调用。加密算法是固定位移的单字节异或加密,如下所示。解密过程中还包括诱饵指令,进一步混淆代码,但没有实际用途。
异或解密
栈内加密字符串
加密 Shellcode 中的敏感字符串都被异或加密作为局部变量嵌入函数体中,甚至会使用多字节异或密钥进行字符串解密。将字符串存储在栈中可以更轻松地隐藏特定信息,当栈更新时其内容将从内存中被删除。如下所示,恶意软件解密字符串获得 NtWriteVirtualMemory 后调用其 API。
加密变量
跳转指令
某些函数包含 call 或 jmp 指令,跳转到另一个函数内的地址,这使得反汇编程序对函数体产生错误的汇编。
如下所示。用 IDA 查看 14000770E+2 处无法得到正确的汇编输出。
调用跳转
该地址被 IDA 认为在不同函数中间,140007710 甚至不会出现。
解析错误
手动标记函数起始位置后,IDA 才能够正确进行反汇编。
修正解析结果
反汇编的隐藏函数位于 __scrt_common_main_seh
函数中,调用的目标是解密和执行内嵌加载程序 Shellcode 的代码。但该函数是标准 C 编译器生成的,旨在启动 WinMain/main 函数。换句话说,此处不应该出现自定义代码。从 WinMain 开始的程序流程就完全被改变了。
返回地址混淆
负责加载和执行此前提到的 Shellcode 的代码也通过栈操作执行返回地址混淆。如下所示,代码中可以看到返回地址如何指向 __scrt_common_main_seh+14
。在最后一个函数调用后,通过不正确的指令操作栈。这会使函数到达 retn 指令时,栈指向解密 Shellcode 地址作为返回地址,这种技术主要阻止分析人员进行分析。
原始返回地址
实际返回地址
控制流图混淆
Shellcode 函数还使用了控制流图混淆,在无限循环中包含大量 Switch 语句。Switch 语句由变量进行控制,看似随机的值来选择下面要执行的分支。这种混淆方式使得不进行动态分析就不可能知道 Switch 语句是否会被执行。
控制流图混淆如下所示:
控制流图混淆
调试器检测
Loader 使用了三种方法检测调试器,一旦发现就会执行非法指令触发崩溃。
- 检查已知调试器进程名称列表。通过 SystemProcessInformation (0x5) 调用 NtQuerySystemInformation 获取正在运行的进程列表。检查的进程包括:
Ida64.exe
Ida.exe
DbgX.Shell
Windbg.exe
X32dbg.exe
X64dbg.exe
Olldbg.exe
进程检查
- 调用 NtQueryInformationProcess 并使用未记录的 0x1e 作为 ProcessInformationClass 参数的值来查找附加到正在运行进程的调试器。如果存在会返回调试对象
NtQueryInformationProcess (in: ProcessHandle=0xffffffffffffffff, ProcessInformationClass=0x1e, ProcessInformation=0x26ce8ff788, ProcessInformationLength=0x8, ReturnLength=0x26ce8ff788 | out: ProcessInformation=0x26ce8ff788, ReturnLength=0x26ce8ff788) returned 0xc0000353
- 通过使用 SystemKernelDebuggerInformation (0x23) 系统信息类调用 NtQuerySystemInformation 来查找内核调试器的存在
NtQuerySystemInformation (in: SystemInformationClass=0x23, SystemInformation=0x26ce8ff388, Length=0x2, ResultLength=0x0 | out: SystemInformation=0x26ce8ff388, ResultLength=0x0) returned 0x0
一旦检测到调试器,不仅触发自身程序崩溃,还会将 WinHttpConnect 替换为自己代码的入口点。这样 Loader 就不能正常加载库文件,也就不能正常连接 C&C 服务器,避免暴露。
代码变更
文件检查
Loader 检测以下几个文件是否存在,存在文件的话即会退出:
- C:\temp\diskpartScript.txt
- C:\Users\Admin\My Pictures\My Wallpaper.jpg
- C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
执行直接系统调用
攻击者尽量避免调用 Windows NT API,直接执行自己实现的系统调用。攻击者创建了多个 NT API 的 Wrapper,每个 NT API 都对应一个 Wrapper。如下所示,为具有四个参数的 NT API Wrapper:
NT API Wrapper
Wrapper 解析 NtQuerySystemInformation,后续通过 jump_to_syscall 指令将给定的系统调用号移动到 EAX 并执行跳转 NtQuerySystemInformation+12。这样完全避免使用 NT API,也就绕过了很多检测 Hook 点位,也就不会在执行日志中现形。
jump_to_syscall 函数体
jmp 指令跳转 syscall 指令
Payload
分析人员只观察到了使用相同 CFG 混淆的 Cobalt Strike,很可能是出自相同攻击者之手。但其中并不包含反调试和反虚拟机技术,可能攻击者认为 Loader 已经确认过。
恶意样本模拟 Kubernetes 流量,向 /api/v1/pods 发起 GET 请求。如果 C&C 服务器没有响应或者不是预期响应,Payload 就会无限循环 ping C&C 服务器。
C&C 请求
头字段 X-Method 有三个可能值:
- con:回连上线
- snd:回传信息
- rcv:接收任务
Cobalt Strike 读取 C&C 服务器的响应信息,进行检查:
- HTTP 响应代码 200
- 存在 X-Session
满足条件时,恶意软件收集失陷主机信息并通过 /api/v1/pods 的 POST 请求回传 C&C 服务器。收集的信息很多,例如用户名、计算机名称、ACP、OEMCP 与网络接口 IP 地址等。
收集信息
窃取的信息会加密发送:
失陷主机信息
窃取系统信息后,向 C&C 服务器发送的 HTTP GET 请求再执行任务。通过 C&C 信道以加密形式发送要执行的指令,加密算法存在大量的位运算。
加密方式
Win32 API 混淆
因为 Payload 需要与位置无关,因此 Win API 导入需要动态解析。恶意软件在内存中创建包含所需的所有 API 函数地址的表,并不存储函数的直接地址,而是存储 ~(_DWORD) api_addr & 0xCAFECAFE | api_addr & 0xFFFFFFFF35013501 的结果值。
存储地址
调用 API 前要撤销此操作,API 调用如下所示:
展开并调用
结论
SquidLoader 明显具备很强的对抗检测能力,两年多一直使用相似的攻击手段针对讲中文的受害者。