01 前言
近年来,Rust语言开始受到恶意软件开发者的青睐,不同的恶意软件家族已经使用Rust语言来重构恶意软件,例如Buer家族银行木马,BlackCat和Hive家族勒索等。Rust受到恶意软件开发者的青睐可能有开发效率,性能,内存安全等方面的因素,但最重要的使用rust编译出来的恶意软件会更容易逃避终端安全软件的静态检测。
可以预知,未来使用Rust编写的恶意软件将会越来越多,想要更好的应对这些未知威胁,我们必须做到知己知彼,才能在这场无硝烟的战争中不落下风。
为了快速熟悉Rust在编写恶意软件中的手法,我这里选择的是开源红队C2框架Link进行分析。
02 介绍
Link是基于Rust语言开发的跨平台C2框架,支持HTTPS协议监听,在技术点上实现了sRDI,内存注入,逃避检测等功能,是一个小而美的C2框架。
源码地址:https://github.com/postrequest/link
03 使用
作者已经提供了kali安装的脚本,在安装过程中会安装rust编译环境和编译依赖,并配置编译生成可执行文件。
3.1主菜单
运行编译好的link,开启https监听,help命令查看主菜单
这里主要关注三个命令即可
generate
生成不同平台的后门
generate(generate,generate-linux,generate-osx)
Use: generate ip/domain:port
sharp
下载https://github.com/Flangvik/SharpCollection工具包后续使用
links
会话展示
3.2 会话功能
links -i id进入当前会话
可根据命令执行具体操作
04 源码分析
工程源码布局
4.1通信
控制端和implant之间一共通过三种请求实现整体的上线和任务收发
Get请求: https://{}/js
Post请求: https://{}/static/register
Post请求: https://{}/static/get
控制端
在spawn.rs中的spawn_server函数中创建线程开启https监听
开启https监听时配置路由,控制端收到请求具体的处理如下
implant
implant向控制端发的请求很简单,分为三个过程
建立通信
首先尝试发送get请求和服务器建立连接当发送的请求得到服务器的回应,发送上线包
上线数据
获取用户名,主机名,ip,外网ip(未实现),平台类型,进程pid,将数据json处理后通过post请求发送到服务器,服务器确认上线后会返回一个请求id(x_request_id)
任务接收
在请求头中加入请求idimplant每次请求服务端都会发一个新的请求id,用于implant下次请求封装的任务数据
HTTP数据流
为了更清晰地看到客户端和服务端的通信,去除代码中的ssl加密,转换成http监听,使用wireshark抓包查看http流数据从建立连接到查询任务
当控制端下发任务
4.2 implant功能
在implant中,除windows外,link对其他平台只实现了shell等几个简单的功能,因此这里只分析windows下的implant
逃避检测
windows implant在启动时会重新映射kernel32.dll和ntdll.dll的.text段,以防止终端安全软件通过注库进行hook,部分代码片段如下
内存dump
Link用的内存dump是用rust重新实现的,在in_memory_dump函数中,利用NtGetNextProcess函数获取要dump的进程句柄,然后使用MiniDumpWriteDump函数进行dump
执行shellcode
创建进程通过APC注入shellcode
进程注入
创建远程线程注入shellcode
4.3控制端功能
控制端的主要功能都和生成shellcode有关
下发任务时使用donut生成shellcode传给implant加载执行
生成implant时使用sRDI生成shellcode形式的implant
donut
调用donut生成shellcode代码
sRDI
link将sRDI中的代码用rust实现,能够将dll以shellcode的形式实现反射dll注入。link sRDI的shellcode构成:bootstrap(保存dll信息的shellcode) + rdiShellcode(反射dll注入shellcode) + dllBytes(原始dll数据) + userData(标志)
(这里以64位为例进行分析)
保存dll信息的shellcode
第一段shellcode长度很短,主要是保存dll的一些信息
结合代码去看逻辑很清晰
反射dll注入shellcode
这里link中的这段shellcode生成的代码比sRDI的shellcode短了不少,应该是对sRDI的代码经过精简的,不过原理都是PE加载,大致流程都是通过PEB获取所需函数->为要加载的dll分配内存->拷贝PE头部和区段(内存对齐粒度)->修复IAT->修复重定位->调用入口点->调用导出函数(这段shellcode对PE加载处理的更细致,也考虑了TLS和注册异常处理等)
通过PEB获取所需函数,shellcode中hash对应的函数
对PE文件做校验
循环获取每个区段的虚拟地址和文件对齐长度
根据sizeofheaders向新申请的内存空间复制DOS头(根据flags只保留e_lfanew)+NT头+区段头
循环复制区段
修复IAT
修复重定位并更新重定位后的代码
调用入口点完成加载并调用dll导出函数main
05 总结
Link可以称得上是麻雀虽小五脏俱全,整体上有很多值得学习的地方。在分析Link的过程中,不仅看到了Rust第三方包obfstr混淆字符串的便携强大,也感受到了逆向Rust代码的痛苦,现有IDA等反编译工具对Rust二进制元数据解析支持做的不够,导致大部分用户代码逻辑都被堆叠在一起,函数边界不清晰,看起来十分混乱,在面对杀软检测和逆向分析时具有天然对抗优势。
Rust恶意软件是一个新威胁,新挑战。在这场较量中,作为安全人员的我们没有理由让步,现在要做的就是时刻准备,等待接招。