作者:360威胁情报中心

一、综述

2017年9月18日,Piriform 官方发布安全公告,公告称该公司开发的 CCleaner version 5.33.6162 和 CCleaner Cloud version 1.07.3191 中的 32 位应用程序被植入了恶意代码。被植入后门代码的软件版本被公开下载了一个月左右,导致百万级别的用户受到影响,泄露机器相关的敏感信息甚至极少数被执行了更多的恶意代码。

CCleaner 是独立的软件工作室 Piriform 开发的系统优化和隐私保护工具,目前已经被防病毒厂商 Avast 收购,主要用来清除 Windows 系统不再使用的垃圾文件,以腾出更多硬盘空间,它的另一大功能是清除使用者的上网记录。自从2004年2月发布以来,CCleaner 的用户数目迅速增长而且很快成为使用量第一的系统垃圾清理及隐私保护软件。而正是这样一款隐私保护软件却被爆出在官方发布的版本中被植入恶意代码,且该恶意代码具备执行任意代码的功能。

这是继 Xshell 被植入后门代码事件后,又一起严重的软件供应链攻击活动。360威胁情报中心通过对相关的技术细节的进一步分析,推测这是一个少见的基于编译环境污染的软件供应链攻击,值得分享出来给安全社区讨论。

二、后门技术细节分析

恶意代码功能

被植入了恶意代码的 CCleaner 版本主要具备如下恶意功能:

  1. 攻击者在CRT初始化函数 __scrt_get_dyn_tls_init_callback() 中插入了一个函数调用,并将此函数调用指向执行另一段恶意代码。
  2. 收集主机信息(主机名、已安装软件列表、进程列表和网卡信息等)加密编码后通过HTTPS协议的POST请求尝试发送到远程IP:216.126.225.148:443,且伪造HTTP头的HOST字段为:speccy.piriform.com,并下载执行第二阶段的恶意代码。
  3. 若IP失效,则根据月份生成DGA域名,并再次尝试发送同样的信息,如果成功则下载执行第二阶段的恶意代码。
植入方式推测

根据360威胁情报中心的分析,此次事件极有可能是攻击者入侵开发人员机器后污染开发环境中的 CRT 静态库函数造成的,导致的后果为在该开发环境中开发的程序都有可能被自动植入恶意代码,相应的证据和推论如下:

1、被植入的代码位于用户代码 main 函数之前

main 函数之前的绿色代码块为编译器引入的 CRT 代码,这部分代码非用户编写的代码。

2、植入的恶意代码调用过程

可以看到 CRT 代码 sub_4010CD 内部被插入了一个恶意 call 调用。

3、被植入恶意代码的 CRT 代码源码调用过程

通过分析,我们发现使用VS2015编译的Release版本程序的CRT反汇编代码与本次分析的代码一致,调用过程为:

_mainCRTStartup --> __scrt_common_main_seh --> __scrt_get_dyn_tls_dtor_callback --> Malicious call

4、CCleaner中被修改的 __scrt_get_dyn_tls_init_callback() 和源码对比

基于以上的证据,可以确定的是攻击者是向 __scrt_get_dyn_tls_init_callback() 中植入恶意源代码并重新编译成 OBJ 文件再替换了开发环境中的静态链接库中对应的 OBJ 文件,促使每次编译 EXE 的过程中,都会被编译器通过被污染的恶意的 LIB/OBJ 文件自动链接进恶意代码,最终感染编译生成的可执行文件。

__scrt_get_dyn_tls_init_callback() 函数位于源代码文件dyn_tls_init.c中。

三、攻击技术重现验证

编译环境的攻击面

通过分析发现,如果要向程序CRT代码中植入恶意代码,最好的方式就是攻击编译过程中引入的CRT静态链接库文件,方法有如下三种:

  1. 修改 CRT 库文件源码,重新编译并替换编译环境中的 CRT 静态库文件(LIB)
  2. 修改 CRT 库文件中某个 OBJ 文件对应的C源码,重新编译并替换 LIB 中对应的 OBJ 文件。
  3. 修改 CRT 库文件中某个 OBJ 文件的二进制代码,并替换 LIB 中对应的 OBJ 文件。
CRT运行时库

C运行时库函数的主要功能为进行程序初始化,对全局变量进行赋初值,加载用户程序的入口函数等。

定位CRT源代码

我们以 VS2008 为例,编写一个功能简单的 main 函数如下:

#include "stdafx.h"

int main(int argc, _TCHAR* argv[])
{
         printf("%d\n", 1);
         return 0;
}

在 main 函数结尾处设置断点,使用 /MD 编译选项编译调试运行

切换到反汇编代码并执行到 main 函数返回:

返回后查阅源码可以看到对应的 CRT 源代码为:crtexe.c

源代码路径:

D:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src\crtexe.c

定位 CRT 静态链接库

参考 MSDN 我们知道,在 VS2008 中,使用 /MD 编译选项编译 Release 版本的程序引用的 CRT 静态库为 msvcrt.lib,文件路径为:

D:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\lib\msvcrt.lib

四、LIB/OBJ文件介绍

以 VS2008 中的 msvcrt.lib 为例

LIB

这里介绍静态库 LIB 文件,是指编译器链接生成后供第三方程序静态链接调用的库文件,其实是单个或多个 OBJ 通过 AR 压缩打包后的文件,内部包含 OBJ 文件以及打包路径信息,比如 msvcrt.lib 文件解压后得到的部分OBJ文件路径如下:

可以看到,msvcrt.lib 解压后确实也有 CRT 对应的 OBJ 文件:crtexe.obj等。

OBJ

源代码编译后的 COFF 格式的二进制文件,包含汇编代码信息、符号信息等等,编译器最终会将需要使用的 OBJ 链接生成 PE 文件,crtexe.obj 文件格式如下:

五、攻击CRT运行时库

了解了 CRT 运行时库的编译链接原理,我们可以知道,使用/MD编译选项编译的 main 函数前的C运行时库函数在静态链接过程中是使用的 msvcrt.ib 中的 crcexe.obj 等进行编译链接的,并且源代码中定义不同的 main 函数名称,编译器会链接 msvcrt.lib 中不同的 OBJ 文件,列举部分如下表所示:

修改crcexe.obj

我们以 VS2008 中编译 main() 函数为例,如果修改 msvcrt.lib 中的 crcexe.obj 的二进制代码,比如修改源码并重编译crcexe.c 或者直接修改 crcexe.obj,再将编译/修改后的 crcexe.obj 替换 msvcrt.lib 中对应的 OBJ,最后将VS2008 中的 msvcrt.lib 替换,那么使用/MD编译选项编译的所有带有 main() 函数的 EXE 程序都会使用攻击者的crcexe.obj 编译链接,最终植入任意代码。

为展示试验效果,我们通过修改 crcexe.obj 中 main 函数调用前的两个字节为 0xCC,试验效果将展示编译的所有 EXE 程序 main 调用前都会有两条 int3 指令:

crcexe.objmsvcrt.lib 中的路径:

f:\dd\vctools\crt_bld\SELF_X86\crt\src\build\INTEL\dll_obj\crcexe.obj

替换 msvcrt.lib 中的 crcexe.obj

替换 msvcrt.lib 中的 OBJ 文件需要两步,这里直接给出方法:

  1. 移除 msvcrt.lib 中的 OBJ 文件,使用VS自带的LIB.EXE移除crcexe.obj:lib /REMOVE: f:\dd\vctools\crt_bld\SELF_X86\crt\src\build\INTEL\dll_obj\crcexe.obj msvcrt.lib

  2. 向msvcrt.lib中插入修改后的crcexe.obj文件,使用VS自带的LIB.EXE插入污染后的crcexe.obj:lib msvcrt.lib f:\dd\vctools\crt_bld\SELF_X86\crt\src\build\INTEL\dll_obj\crcexe.obj

编译过程自动植入恶意代码

将替换了 crcexe.objmsvcrt.lib 覆盖 VS 编译器中的 msvcrt.lib

D:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\lib\msvcrt.lib

重新编译执行我们的测试程序,可以看到在 main 函数执行前的两条插入的 int3 指令:

六、结论与思考

2017年9月初360威胁情报中心发布了《供应链来源攻击分析报告》,总结了近几年来的多起知名的供应链攻击案例,发现大多数的供应链攻击渠道为软件捆绑。通过污染软件的编译环境的案例不多,最出名的就是2015年影响面巨大的Xcode开发工具恶意代码植入事件,从当前的分析来看,CCleaner也极有可能是定向性的编译环境污染供应链攻击。以下是一些相关的技术结论:

  • 针对 LIB 文件攻击方式可以通过重编译源码或者修改 OBJ 二进制代码这两种方式实现。
  • 修改 OBJ 二进制代码实现对LIB文件的代码注入不同于修改源码,此方法理论上可用于注入任何静态链接库 LIB。
  • 只需按照 OBJ 文件格式规范即可注入任意代码(shellcode),比如在 OBJ 中新增/扩大节,填充 shellcode 并跳转执行。
  • 此攻击方法可以在用户代码执行前(CRT)、执行中(调用库函数)、甚至执行结束后执行植入的恶意代码,并且由于恶意代码并不存在于编写的源代码中,所以很难被开发人员发现。
  • 攻击者完全可以植入某个深层次调用的开发环境下的静态库文件,以达到感染大部分开发程序并持久化隐藏的目的。
  • 使用源代码安全审查的方式无法发现这类攻击

由于这类定向的开发环境污染攻击的隐蔽性及影响目标的广泛性,攻击者有可能影响 CCleaner 以外的其他软件,我们有可能看到攻击者造成的其他供应链污染事件。

七、参考链接

https://msdn.microsoft.com/en-us/library/abx4dbyh(v=vs.90).aspx


Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/404/