前言
2020年12月,SolarWinds 攻击事件引发全球的关注(<https://us-
cert.cisa.gov/ncas/alerts/aa20-352a>),攻击团队在 2020年上旬通过对 SolarWinds Orion
产品实现供应链攻击,导致诸多厂商被攻击,造成了不可估量的损失。
这种国家间的 APT 攻击包含了大量的技术细节,其中供应链攻击的实现,也就是 SUNBURST 后门植入这一块引起了我极大的兴趣。
2021年1月,网上公开了 SUNBURST 后门植入的分析,后续又有安全研究者对植入细节进一步的优化,根据这些内容我展开了对 dll
劫持的学习和研究。本文对 dll 劫持进行了详细的介绍,并模仿 SUNBURST 后门植入的方法,尝试对 C 编译器实现"供应链攻击"。
本文测试环境为Windows7 x64 sp1
,开发环境为:MinGW-x64(gcc-8.1.0) + Python3.6
。
后门植入和优化
我们可以简单看看 SUNBURST 后门植入的过程(<https://www.crowdstrike.com/blog/sunspot-malware-
technical-analysis/>):由一个名为taskhostsvc.exe
的程序进行完成,该程序通过计划任务设置随主机启动运行。
taskhostsvc.exe
启动后通过创建互斥体保证只有一个实例在运行,然后每秒从进程中搜索MsBuild.exe
进程(Microsoft
Build Engine),找到后通过读取MsBuild.exe
的内存,从命令行参数中获取构建项目的目录路径;
随后在项目目录下寻找 Orion
产品的InventoryManager.cs
源码文件,并使用包含有恶意代码的源码文件进行替换,等待MsBuild.exe
编译完成,最后再还原该文件,完成后门的植入;如下图
SUNBURST后门植入的流程
当然这个过程中还需要很多技术细节来保证后门与原始项目代码之间的兼容性,以及植入过程的隐蔽性等等;后续就有安全研究者说到在 APT
攻击中上文中的植入过程不够完美,比如计划任务和周期性的进程扫描很容易暴露攻击行为,其次监控MsBuild.exe
运行到最终替换源码文件,这中间的执行时间可能影响后门植入的成功率。
安全研究者提出了使用 dll
劫持来优化后门植入的过程(<https://www.a12d404.net/ranting/2021/01/17/msbuild-
backdoor.html>),其研究过程发现MSBuild.exe
启动过程中会去优先加载指定目录的
dll,如下:
MSBuild优先加载的部分dll文件(ref:https://www.a12d404.net/ranting/2021/01/17/msbuild-
backdoor.html)
如果我们将恶意 dll 重命名并放置在这些"load-not-found-dll"
的路径下,就可以实现 dll
劫持,执行我们的恶意代码,原文作者根据这种方式编写代码进行了演示。相比于taskhostsvc.exe
,使用这种方式就不需要额外的进程来进行监控了,并且
dll 在程序执行前加载、在程序执行后释放,这个时间点也很适合用于对程序进行控制和清理。
dll劫持概要
dll(动态链接库)作为 windows 的函数库,有助于促进代码的模块化、代码重用、有效的内存使用并减少磁盘空间;一个应用程序运行时可能需要依赖于多个
dll 的函数才能完成功能,如果控制其中任一 dll,那么便可以控制该应用程序的执行流程。
要学习 dll 劫持,那必须先了解 dll 的搜索顺序,这也是攻防的兵家必争之地,微软近年来也不断的在这一块进行加固,对于桌面程序(非UWP应用)目前默认
dll 的搜索顺序为(<https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-
library-search-order>):
1.应用程序加载的目录
2.系统目录,使用 GetSystemDirectory 获取该路径
3.16 位系统目录
4.Windows 目录,使用 GetWindowsDirectory 获取该路径
5.当前目录
6.PATH 环境变量中列出的目录
默认情况下
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
处于开启状态;如果手动设置为
0,关闭该安全选项,搜索顺序为:在以上顺序基础上,将5.当前目录
修改至2.系统目录
的位置,其他顺移。
应用程序加载 dll 时如果仅指定 dll 名称时,那么将按照以上顺序搜索 dll 文件;不过在加载之前还需要满足以下两条规范:
1.当内存中已加载相同模块名称的 dll 时,系统将直接加载该 dll,不会进行搜索;除非设置了 dll 重定向选项
2.如果要加载的 dll 模块属于 Known DLLs,系统直接加载系统目录下的该 dll,不会进行搜索;Known DLLs
列表:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
由于 dll 迭代更新可能出现不兼容的问题,微软提出 dll 重定向解决方案,以便应用程序可以自定义选择加载 dll
在了解以上基础内容后,在 dll 搜索路径上对文件进行替换,那么便可以实现 dll 劫持。
微软在 dll 这一块所做的安全加固详情可以参考:<https://docs.microsoft.com/en-
us/windows/win32/dlls/dynamic-link-library-security>
dll函数转发
使用恶意 dll 替换原文件,应用程序便可以加载我们的 dll 并执行恶意代码,但是应用程序运行依赖于 dll 提供的函数,恶意 dll
必须提供相同的功能才能保证应用程序的正常运行;所以我们先来了解下 dll 函数转发。
1.手动转发
当 dll 的导出函数比较少时,我们可以按照正常的 dll 开发流程,逐个定义函数名称,然后在函数内部使用LoadLibrary()
函数调用原 dll
的对应函数完成功能,如下:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AhHB20VR-1691632044505)(https://image.3001.net/images/20210916/1631780646_6142ff26ddd4ac925104c.png!small)]
LoadLibrary转发函数
2.def文件
当 dll 的导出函数太多时,我们就不能手动转发了,使用模块定义(.def)文件编写导出函数的信息,由链接器自动实现函数转发,细节可以参考
<https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-
def-files?view=msvc-160>。
我们尝试生成version.dll
的恶意
dll:在test.c
文件中编写恶意代码插入到DllMain
的执行流程中,并在test.def
中编写函数转发规则:
使用dll模块定义进行函数转发
随后进行编译生成 dll 文件,gcc 编译如下:
gcc -Wall -shared test.c test.def -o version.dll
在.def
文件中我们将原始的文件命名为version_origin.dll
,当应用程序运行时将加载我们恶意的version.dll
,当调用函数时,将由恶意的version.dll
进行函数转发:
something.exe => version.dll (malware) => version_origin.dll
这里我编写了个 Python 脚本可以根据 dll
自动生成模块定义文件dllproxy_def_generate.py,使用如下:
自动生成模块定义文件
部分 dll 导出函数没有导出名称,只有导出序号,Gcc 和 Tcc 不支持按序号导出的函数转发,读者遇到的话可以使用 VisualStdio
3.pragma预处理
除了上文使用模块定义文件来实现函数转发,还可以使用pragma
来实现,这一块细节可以参考 <https://docs.microsoft.com/en-
us/cpp/preprocessor/comment-c-
cpp?view=msvc-160>,同样以version.dll
为例,函数转发的源码如下:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dB2u1zqA-1691632044507)(https://image.3001.net/images/20210916/1631780721_6142ff71e21265e55954d.png!small)]
pragma实现dll函数转发
不过
pragma
关键词只有 Microsoft 编译器提供。
路径劫持
根据以上知识,我们可以自由的生成恶意 dll 文件,并且通过函数转发使其调用原始的 dll
函数,完全不会影响应用程序的正常运行。除此之外,恶意代码我们一般可以添加到DllMain
中,这样在加载 dll
时便可以触发代码,或者添加到指定函数中,精确劫持程序流程,这取决于实际的场景。到这里我们的恶意 dll 就已经准备就绪了。
根据 dll 的类型,我们可以将劫持大致可以分为两种方式:
1.自定义dll
有些应用程序使用了自定义 dll,这个 dll 是该应用程序特有的,只会被该程序加载和使用。该 dll 可能放置在应用程序同目录下,或者 PATH
环境变量中,或者特定目录通过LoadLibrary([路径])
来加载。
这种我们使用恶意 dll 替换目标文件,然后再将原始 dll
重命名并放置在应用程序同目录下(以便函数转发可以顺利进行),当应用程序启动时就可以加载我们的恶意 dll。
2.公共dll
当然我们还可以对公共 dll 进行劫持(比如系统 dll ),但是公共 dll
一般都会提前被其他进程加载,当新的应用程序需要加载时,将直接从内存进行加载和调用,如果我们使用恶意 dll 替换了公共 dll,需要通过重启才能生效。
比如下面是我们使用恶意 dll 替换msvcrt.dll
,恶意 dll
在加载时输出应用程序路径,重启后可以看到:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l1mL57ZW-1691632044508)(https://image.3001.net/images/20210916/1631780735_6142ff7fa699dbe76153b.png!small)]
劫持系统dll示例
由于是公共 dll,那么所有的程序都会加载恶意 dll,这种方法可以用于监控、蜜罐等场景。
1.替换系统dll,可以用普通用户修改文件为拥有者,然后再设置读写权限,就可以修改和替换文件了
2.不能劫持ntdll.dll / kernel32.dll
等非常底层的 dll,因为这些 dll 实现了程序装载、函数转发等功能
dll重定向劫持
在我们的学习过程中发现,有些应用程序只依赖了系统 dll,并且这些 dll 已经被其他程序加载了,比如 MinGW(gcc)
只依赖了kernel32.dll
和msvcrt.dll
,除了上文公共 dll 劫持,还有其他更好的办法吗?
在「0x02 dll劫持概要」中我们还提到一种特例:dll 重定向(<https://docs.microsoft.com/en-
us/windows/win32/dlls/dynamic-link-library-redirection>),当模块名称相同的 dll
已经被其他应用程序加载到内存中时,可以使用该方法强制加载指定的 dll 文件。通过这种方式,也可以实现 dll 劫持。
dll重定向默认为关闭状态,我们在注册表中HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
添加DevOverrideEnable (DWORD)
字段并设置为
1,来开启该功能,重启后生效。
注册表开启dll重定向
我们有两种方式来使用 dll 重定向:
1. .local
在应用程序同目录下,创建AppName.exe.local
的目录,应用程序启动时将优先从该目录下加载 dll 文件。
我们编写了个HelloWorld
的 C 程序,生成恶意的msvcrt.dll
进行演示,目录结构为:
.
├── test.exe.local
│?? ├── msvcrt.dll
│?? └── msvcrt_origin.dll
├── helloworld.c
└── test.exe
运行演示如下:
local重定向示例
2.manifest
还可以使用 manifest 配置文件(xml文件),优先级高于.local
,详细可以参考
<https://docs.microsoft.com/en-us/windows/win32/sbscs/application-
manifests>,构建目录结构如下:
.
├── helloworld.c
├── msvcrt_origin.dll
├── msvcrt.dll
├── msvcrt.dll.manifest
├── test.exe
└── test.exe.manifest
其中 manifest
文件内容为:
manifest内容示例
同样可以加载恶意 dll 文件。
Tcc劫持实现
Tcc(Tiny C Compiler) 是一个相当小的 C 编译器,我们从简单的开始对 C 编译器进行"供应链攻击"。
我这里使用了Tcc 0.9.27
版本,结合逆向分析可以确定tcc.exe
依赖了同目录下的libtcc.dll
文件,直接替换该 dll 文件即可。
我们模仿 SUNBURST 后门植入的方法,编写恶意代码 demo 如下(libtcc.c+
libtcc.def):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ofPqnkX-1691632044510)(https://image.3001.net/images/20210916/1631780779_6142ffabb528b4f074e7a.png!small)]
Tcc劫持代码demo
编译后使用恶意 dll 替换libtcc.dll
,并将原始文件重命名为libtcc_origin.dll
,运行演示如下:
Tcc劫持演示
可以看到通过 Tcc 编译的程序,执行时触发了恶意代码backdoor
。
Gcc劫持实现
我们再来尝试下
Gcc(MinGW),通过分析发现他只依赖了kernel32.dll
和msvcrt.dll
,那么这里我们劫持msvcrt.dll
文件,使用
dll 重定向的方法让 Gcc 加载恶意 dll 文件。
使用 Tcc 中的测试代码进行编译,然后在 Gcc 目录下添加gcc.exe.local
文件夹,并将msvcrt.dll / msvcrt_origin.dll
放在文件夹下,如下:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MOI8wGIt-1691632044512)(https://image.3001.net/images/20210916/1631780799_6142ffbfb71685d27bf51.png!small)]
Gcc目录下dll重定向文件夹
运行演示如下:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wxta2RoS-1691632044512)(https://image.3001.net/images/20210916/1631780810_6142ffcaaea192cc644a6.png!small)]
Gcc劫持演示
总结
在这里感谢 DAWU@知道创宇404实验室 小伙伴在我学习研究过程中提供的帮助。本文从 dll 劫持的基础出发,逐步讲解和演示 dll 劫持,对 dll
劫持的部分场景和利用进行说明,最后模仿了 SUNBURST 后门植入的方法实现了对 C 编译器的"供应链攻击"。
实际上文中提到的对编译器进行"供应链攻击"的方法还可以进一步优化,因为我们的方法会重新写入文件,从而会修改文件的写入时间,可能会暴露攻击行为;我们还可以通过逆向分析编译器的执行流程,更加精确的劫持读文件的函数,在内存中植入恶意代码,读者可以自行尝试下。
不过 dll
劫持的攻防对抗已经发展很长时间了,微软在保证功能的前提下,已经提供了较为完善的防御措施;对于上文介绍的劫持方法和场景,防御时可以按照文中的技术细节如:路径、注册表、文件进行排查。
References:
https://us-cert.cisa.gov/ncas/alerts/aa20-352a
https://www.crowdstrike.com/blog/sunspot-malware-technical-analysis/
https://www.a12d404.net/ranting/2021/01/17/msbuild-backdoor.html
<https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-
search-order>
<https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-
redirection>
<https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-
security>
<https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-
def-files?view=msvc-160>
<https://docs.microsoft.com/en-us/cpp/preprocessor/comment-c-
cpp?view=msvc-160>
<https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-
redirection>
https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests
<https://www.exploit-db.com/docs/english/13140-api-interception-via-dll-
redirection.pdf>
https://github.com/erocarrera/pefile
https://github.com/tothi/dll-hijack-by-proxying
<https://stackoverflow.com/questions/37252457/is-there-a-way-to-make-
windows-7-x64-load-ntdll-dll-from-local-directory-not-sy>
作者:0x7F@知道创宇404实验室
via-dll-
redirection.pdf>
https://github.com/erocarrera/pefile
https://github.com/tothi/dll-hijack-by-proxying
<https://stackoverflow.com/questions/37252457/is-there-a-way-to-make-
windows-7-x64-load-ntdll-dll-from-local-directory-not-sy>
作者:0x7F@知道创宇404实验室
学习网络安全技术的方法无非三种:
第一种是报网络安全专业,现在叫网络空间安全专业,主要专业课程:程序设计、计算机组成原理原理、数据结构、操作系统原理、数据库系统、 计算机网络、人工智能、自然语言处理、社会计算、网络安全法律法规、网络安全、内容安全、数字取证、机器学习,多媒体技术,信息检索、舆情分析等。
第二种是自学,就是在网上找资源、找教程,或者是想办法认识一-些大佬,抱紧大腿,不过这种方法很耗时间,而且学习没有规划,可能很长一段时间感觉自己没有进步,容易劝退。
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
第三种就是去找培训。
接下来,我会教你零基础入门快速入门上手网络安全。
网络安全入门到底是先学编程还是先学计算机基础?这是一个争议比较大的问题,有的人会建议先学编程,而有的人会建议先学计算机基础,其实这都是要学的。而且这些对学习网络安全来说非常重要。但是对于完全零基础的人来说又或者急于转行的人来说,学习编程或者计算机基础对他们来说都有一定的难度,并且花费时间太长。
第一阶段:基础准备 4周~6周
这个阶段是所有准备进入安全行业必学的部分,俗话说:基础不劳,地动山摇
第二阶段:web渗透
学习基础 时间:1周 ~ 2周:
① 了解基本概念:(SQL注入、XSS、上传、CSRF、一句话木马、等)为之后的WEB渗透测试打下基础。
② 查看一些论坛的一些Web渗透,学一学案例的思路,每一个站点都不一样,所以思路是主要的。
③ 学会提问的艺术,如果遇到不懂得要善于提问。
配置渗透环境 时间:3周 ~ 4周:
① 了解渗透测试常用的工具,例如(AWVS、SQLMAP、NMAP、BURP、中国菜刀等)。
② 下载这些工具无后门版本并且安装到计算机上。
③ 了解这些工具的使用场景,懂得基本的使用,推荐在Google上查找。
渗透实战操作 时间:约6周:
① 在网上搜索渗透实战案例,深入了解SQL注入、文件上传、解析漏洞等在实战中的使用。
② 自己搭建漏洞环境测试,推荐DWVA,SQLi-labs,Upload-labs,bWAPP。
③ 懂得渗透测试的阶段,每一个阶段需要做那些动作:例如PTES渗透测试执行标准。
④ 深入研究手工SQL注入,寻找绕过waf的方法,制作自己的脚本。
⑤ 研究文件上传的原理,如何进行截断、双重后缀欺骗(IIS、PHP)、解析漏洞利用(IIS、Nignix、Apache)等,参照:上传攻击框架。
⑥ 了解XSS形成原理和种类,在DWVA中进行实践,使用一个含有XSS漏洞的cms,安装安全狗等进行测试。
⑦ 了解一句话木马,并尝试编写过狗一句话。
⑧ 研究在Windows和Linux下的提升权限,Google关键词:提权
以上就是入门阶段
第三阶段:进阶
已经入门并且找到工作之后又该怎么进阶?详情看下图
给新手小白的入门建议:
新手入门学习最好还是从视频入手进行学习,视频的浅显易懂相比起晦涩的文字而言更容易吸收,这里我给大家准备了一套网络安全从入门到精通的视频学习资料包免费领取哦!
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
![](https://i-blog.csdnimg.cn/blog_migrate/08c3b846393bdaea6409358cbf194580.jpeg)