渗透测试-木马免杀的几种方式
渗透测试-木马免杀的几种方式
0x01前言
免杀就是反病毒技术,,它指的是一种能使病毒木马免于被杀毒软件查杀的技术。由于免杀技术的涉猎面非常广,其中包含反汇编、逆向工程、系统漏洞等黑客技术,所以难度很高,一般人不会或没能力接触这技术的深层内容。其内容基本上都是修改病毒、木马的内容改变特征码,从而躲避了杀毒软件的查杀。
它除了使病毒木马免于被查杀外,还可以扩增病毒木马的功能,改变病毒木马的行为。免杀的基本特征是破坏特征,有可能是行为特征,只要破坏了病毒与木马所固有的特征,并保证其原有功能没有改变,一次免杀就能完成了。
免杀技术也并不是十恶不赦的,例如,在软件保护所用的加密产品(比如壳)中,有一些会被杀毒软件认为是木马病毒;一些安全领域中的部分安全检测产品,也会被杀毒软件误杀,这时就需要免杀技术来应对这些不稳定因素。
0x02查杀方式及原理
杀毒软件基本原理
1. 无害
没有任何可疑行为,没有任何特征符合病毒。
2. 可疑
存在可疑行为:操作注册表,打开powershell,修改用户,操作敏感文件等。
3. 病毒
特征符合病毒。
杀毒软件常用识别方式
1. 静态:
从病毒体中提取的病毒特征码,逐个与程序文件比较。特征码是反病毒公司在分析病毒时,
确定的只有该病毒才可能会有的一系列二进制串,由这些特征可以与其它病毒或正常程序区别
开来。
静态特征码免杀针对kingsoft,macafee、Avira、trendmicro免杀效果比较明显。
静态查杀
:一般根特征码识别到,然后对文件进行特征匹配。
hash值以及二进制特征
这个hash和二进制特征依赖于杀软自身的杀软特征库,举个例子你用cs原生生成的exe或者bin文件是基本上100%被杀软识别的,也就是说原生文件有特征是被各大厂商所识别的,毕竟cs作为以前的主流c2,其特征早就被各大厂商分析透彻了。如何比避免bin文件被识别,最简单的方式就是使用rc4 或者 aes对你的bin文件进行加密。
iat表
这个算是静态查杀的一个核心重点,以分离式为例子,loader.exe+shellcode文件loader内部是不存在shellcode的,这样的好处是不会造成你的pe文件被扫描时内部出现大量的加密字符串提高你的熵值,并且可以立刻排除是shellcode文件导致的你的loader被查杀。回归正题,大部分编译好的exe文件都是存在iat表,它会记录你的调用函数如图:
争议
对于是否给你的exe添加图标,以我的经验是完全不需要的,甚至添加图标会被杀软表示为特征。其次就是签名,之前用过的py盗取签名的方式其实是非常糟糕的,在实践环境中,所以这就是为什么现在基本上使用白加黑的原因,当然这里不得不说360,我个人的对抗环境是不存在360和火绒的和一些国内的杀软,但有朋友希望我做bypass 360时,我发现360基本上通杀白加黑并且system32下的dll劫持基本上都被杀,如果你改了dll名字就是正常的,这样你也就无法加载你的黑dll了,所以对于360,我最终是用loader+bin的形式bypass核晶+鲲鹏,并执行cmd操作。
2. 启发式查杀
启发式查杀是虚拟机引擎和行为检测相结合,通过模拟执行, 分析程序行为的安全检测技术。
启发式查杀(动态查杀)
:主要是对其产生的行为进行检测。
3.云查杀
云安全机制是一种新兴的安全查杀机制,不同的安全厂商的云安全查杀机制不一样。基于云共享特征库扫描机制360 安全卫士,电脑管家,基于主动防御信誉云的扫描机制。
云查杀
:查毒的原理是对文件内容及行为的检测,要实现这个需要唯一确定文件吧,md5?当大文件时,效率较低,随便改个字节都变了,所以就有了提特征(针对所有文件,根据指定位提取唯一信息,速度快)。
- 可构建行为库进行动态查杀
- 可构建日志库对日志库进行动态查杀
- 统计学检测—>构建特征学习模型—>进行动态查获取就好了
0x03 绕过思路总结
面对不同的杀毒引擎,有不同的绕过思路,大体分为以下几种:
1. 如何绕过静态查杀?
1.1 动态调用API
1.1.1 特征码定位;
1.1.2 动态获取API地址;
1.1.3 自己调用;
1.2 代码混淆技术
定位到被查杀的函数块,然后通过 API 乱序调用或者插入一些正常其它API调用, 如释放文件时采用单个字节循环写入。
1.3 底层API替代调用
当模块在进行特殊操作的时候被杀,可以采用调用底层的API接口完成同样的功能,如使用CreateProcessInternalW创建进程,NtQuerySystemInfomation获取系统信息,以及一些其它的Native API。
1.4 加壳保护
自己开发的代码保护工具进行加壳保护。
2.如何突破启发式查杀?
2.1 内存载入解析技术
自己在内存中完成对模块的载入、修复和调用。
2.2 模块二次载入技术
当需要调用一些敏感模块的时候,可以采用在内存二次载入我们的目标模块,然后动态查找EAT 完成函数调用。 启发检测。
0x04 免杀绕过实战:
1. AES 加密shellcode
https://github.com/Ed1s0nZ/GoYiyi
将shellcode进行AES加密之后,用加载器去解密+加载:
aes.go
1 | package main |
loader.go
1 | package main |
将aes加密过的shellcode放到loader里去加载(把shellcode放到数组里是为了不让shellcode单独成为一个参数提高免杀概率,无其他意义。)
defender、360、火绒检测不到,且能正常执行命令。
2. shellcode分离免杀
将shellcode和加载器分离出来,将shellcode加密写入到一个文本、图片、网站body,让loader去访问并加载:
code.go
1 | package main |
loader.go
1 | package main |
上面这个是从参数中获取shellcode加载的方式,如果识别到http,就从url中加载图片的shellcode,如果识别到=====18jokriwow0
后缀,它才会在命令行中去解密并加载shellcode,如果二者条件都没达成,则什么都不执行。
3. Golang代码混淆
看过很多golang免杀的文章,但是很多人都没提到一点,Golang的代码混淆;由于 Golang 的反射等机制,需要将文件路径、函数名等大量信息打包进二进制文件,这部分信息无法被 strip,所以考虑通过混淆代码的方式提高逆向难度,增加免杀的概率。
Github有一个开源的Golang代码混淆项目:https://github.com/burrowers/garble
安装:
1 | go install mvdan.cc/garble@latest |
通过包装 Go 工具链来混淆 Go 代码。需要 Go 1.17 或更高版本。
1 | garble build [build flags] [packages] |
只要在编译时,将go build
替换为garble build
即可,该工具还支持garble test
使用混淆代码运行测试,以及garble reverse
去混淆诸如堆栈跟踪之类的文本。请参阅garble -h
了解最新的使用信息。
混淆前
反编译可以看到函数名等信息清晰可见:
混淆后
混淆后代码大小也从1.6m变为了1.1m。
混淆后微步3/25,检测为安全,打分为10分:
4.UUID免杀
使用Python生成uuid格式:
1 | # shellcode必须是16的倍数,不足用x00补充 |
loader:
1 | [[include]]<Windows.h> |
5.底层API绕过免杀
先看以下这两个loader
loader1.go:
1 | package main |
loader2.go:
1 | package main |
这两个都是Go语言实现的加载aes加密shellcode的 loader,区别在于,第一个loader调用了Windows VirtualProtec api,第二个没有,而第一种,直接编译后会被火绒杀掉,第二种可正常免杀。
loader1.go
loader2.go
0x05 Fuzz Loader源码
如何Fuzz 出loader被查杀的点,在写免杀时尤为重要,因为你不知道问题出在哪里,就没办法解决问题。在fuzz时,要注意我们的木马是静态被杀还是运行时被杀,这有助于我们更快的锁定查杀点。下面讲一下如何通过loader源码定位杀软查杀的点:
静态被杀
举下面这一个分离免杀 loader的例子:
loader.go
1 | package main |
将shellcode通过两次异或,然后再base64加密,将加密过后的shellcode,追加到jpg图片后面,通过loader去加载这个图片里的shellcode,从而上线。
fuzz源代码:
当时怀疑是这里,通过读取到参数(图片路径),直接从图片里接收shellcode去加载,
注释掉这部分:
编译执行,继续报毒:
继续fuzz,将解密这行注释掉:
杀软不在报毒
目标缩小到
1 | shellcode = append(shellcode, decodeBytes[i]^KEY_1^KEY_2) |
因为这只是一个加载器,图片以参数形式加载,而且加载器被静态杀掉,所以与shellcode无关,那问题就只可能出在异或上了
将异或改为加:
1 | shellcode = append(shellcode, decodeBytes[i]+KEY_1+KEY_2) |
这里就锁定了火绒查杀的点:查杀异或,只要换一种加密方式就好了。
如aes:
loader.go
1 | package main |
动态被杀
在写loader时,遇到这样一种情况,defender、360落地不杀,defender云查杀扫描后被杀:
当时loader是这样:
1 | package main |
loader有两种上线方式,首先判断参数中是否有”http”,如果有就去请求链接,否则直接解密并加载shellcode。
想到落地不被杀,云查杀之后被杀,怀疑被跑沙箱,提取了第二种上线方式的动作:
于是修改loader
1 | package main |
在生成加密的shellcode时,会在后缀随机添加一段字符串,只有匹配到该字符串,才运行第二种上线方式,若两者(既不含有”http”又不含有特殊字符)都不成立,则打印a(fmt.Println(“a”))。顺利过defender、360、卡巴等。。。
好了,今天的分享就到这里,有问题的话评论区见。