作者: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 版本主要具备如下恶意功能:
__scrt_get_dyn_tls_init_callback()
中插入了一个函数调用,并将此函数调用指向执行另一段恶意代码。speccy.piriform.com
,并下载执行第二阶段的恶意代码。根据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静态链接库文件,方法有如下三种:
C运行时库函数的主要功能为进行程序初始化,对全局变量进行赋初值,加载用户程序的入口函数等。
我们以 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
参考 MSDN 我们知道,在 VS2008 中,使用 /MD 编译选项编译 Release 版本的程序引用的 CRT 静态库为 msvcrt.lib,文件路径为:
D:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\lib\msvcrt.lib
以 VS2008 中的 msvcrt.lib 为例
这里介绍静态库 LIB 文件,是指编译器链接生成后供第三方程序静态链接调用的库文件,其实是单个或多个 OBJ 通过 AR 压缩打包后的文件,内部包含 OBJ 文件以及打包路径信息,比如 msvcrt.lib
文件解压后得到的部分OBJ文件路径如下:
可以看到,msvcrt.lib
解压后确实也有 CRT 对应的 OBJ 文件:crtexe.obj
等。
源代码编译后的 COFF 格式的二进制文件,包含汇编代码信息、符号信息等等,编译器最终会将需要使用的 OBJ 链接生成 PE 文件,crtexe.obj
文件格式如下:
了解了 CRT 运行时库的编译链接原理,我们可以知道,使用/MD编译选项编译的 main 函数前的C运行时库函数在静态链接过程中是使用的 msvcrt.ib
中的 crcexe.obj
等进行编译链接的,并且源代码中定义不同的 main 函数名称,编译器会链接 msvcrt.lib
中不同的 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.obj
在 msvcrt.lib
中的路径:
f:\dd\vctools\crt_bld\SELF_X86\crt\src\build\INTEL\dll_obj\crcexe.obj
替换 msvcrt.lib
中的 OBJ 文件需要两步,这里直接给出方法:
lib /REMOVE: f:\dd\vctools\crt_bld\SELF_X86\crt\src\build\INTEL\dll_obj\crcexe.obj msvcrt.lib
lib msvcrt.lib f:\dd\vctools\crt_bld\SELF_X86\crt\src\build\INTEL\dll_obj\crcexe.obj
将替换了 crcexe.obj
的 msvcrt.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也极有可能是定向性的编译环境污染供应链攻击。以下是一些相关的技术结论:
由于这类定向的开发环境污染攻击的隐蔽性及影响目标的广泛性,攻击者有可能影响 CCleaner 以外的其他软件,我们有可能看到攻击者造成的其他供应链污染事件。
https://msdn.microsoft.com/en-us/library/abx4dbyh(v=vs.90).aspx