综述
2017年9月18日,Piriform 官方发布安全公告,公告称该公司开发的CCleaner version 5.33.6162和CCleaner Cloud version 1.07.3191中的32位应用程序被植入了恶意代码。被植入后门代码的软件版本被公开下载了一个月左右,导致百万级别的用户受到影响,泄露机器相关的敏感信息甚至极少数被执行了更多的恶意代码。
CCleaner是独立的软件工作室Piriform开发的系统优化和隐私保护工具,目前已经被防病毒厂商Avast收购,主要用来清除Windows系统不再使用的垃圾文件,以腾出更多硬盘空间,它的另一大功能是清除使用者的上网记录。自从2004年2月发布以来,CCleaner的用户数目迅速增长而且很快成为使用量第一的系统垃圾清理及隐私保护软件。而正是这样一款隐私保护软件却被爆出在官方发布的版本中被植入恶意代码,且该恶意代码具备执行任意代码的功能。
这是继Xshell被植入后门代码事件后,又一起严重的软件供应链攻击活动。奇安信威胁情报中心通过对相关的技术细节的进一步分析,推测这是一个少见的基于编译环境污染的软件供应链攻击,值得分享出来给安全社区讨论。
后门技术细节分析
恶意代码功能
被植入了恶意代码的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域名,并再次尝试发送同样的信息,如果成功则下载执行第二阶段的恶意代码。
植入方式推测
根据奇安信威胁情报中心的分析,此次事件极有可能是攻击者入侵开发人员机器后污染开发环境中的CRT静态库函数造成的,导致的后果为在该开发环境中开发的程序都有可能被自动植入恶意代码,相应的证据和推论如下:
1、被植入的代码位于用户代码main函数之前
main函数之前的绿色代码块为编译器引入的CRT代码,这部分代码非用户编写的代码。
2、植入的恶意代码调用过程
可以看到是由CRT代码.text:004D4D2C call sub_4010CD
内部调用的。
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静态链接库文件,方法有如下三种:
- 修改CRT库文件源码,重新编译并替换编译环境中的CRT静态库文件(LIB)
- 修改CRT库文件中某个OBJ文件对应的C源码,重新编译并替换LIB中对应的OBJ文件。
- 修改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文件,列举部分如下表所示:
main函数名称 | 对应的OBJ文件 | 说明 |
main() | crcexe.obj | the startup routine for console apps |
_tmain() | wcrcexe.obj | the startup routine for console apps with wide chars |
WinMain() | crcexew.obj | the startup routine for Windows apps |
wWinMain() | wcrcexe.obj | the startup routine for Windows apps with wide chars |
修改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.obj在msvcrt.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.obj的msvcrt.lib覆盖VS编译器中的msvcrt.lib: D:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\lib\msvcrt.lib
重新编译执行我们的测试程序,可以看到在main函数执行前的两条插入的int3指令:
结论与思考
2017年9月初奇安信威胁情报中心发布了《供应链来源攻击分析报告》,总结了近几年来的多起知名的供应链攻击案例,发现大多数的供应链攻击渠道为软件捆绑。通过污染软件的编译环境的案例不多,最出名的就是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