前言
https://wikileaks.org/ciav7p1/cms/files/BypassAVDynamics.pdf)。虽说是2014年8月所撰写的内容,但其思路和测试方法依然有一定的研究价值,所以将其翻译出来,供大家借鉴学习。
文中部分原文关键字已保留,笔者觉得原文更加容易理解一些。因英语水平有限,许多地方翻译的不是很通顺,请各位海涵。
注意:本篇内容需要一些C语言和windows系统编程方面的知识。
内容目录
一、介绍
二、绕过杀软的理论
1、静态特征分析
2、静态启发式分析
3、动态分析
4、杀软的局限性
三、测试条件
1、本地环境
2、VirusTotal
3、加密的恶意软件
四、复杂的方法
1、代码注入方法
2、RunPE 方法
五、简单但有效的方法
1、The “Offer you have to refuse” method及例子
2、The “I shouldn’t be able to do that!” method及例子
3、The “Knowing your enemy” method及例子
4、The “WTF is that?” method及例子
5、The “Checking the environment” method及例子
6、The “I call myself” method及例子
六、结论
一、介绍
“Antivirus are easy to bypass”,“Antivirus are mandatory in defense in depth”,“This Cryptor is FUD”是当你做一些反病毒安全研究的时候经常听到的句子。我问我自己,绕过杀软真的很简单吗?经过研究后,我得出了跟其他人一样的结论,绕过杀软包括两大步骤:
隐藏那些可能被识别为具有恶意行为的代码,一般使用加密来处理。
用一种不被仿真或者沙箱系统检测为病毒的解密Stub编写的方式。
二、绕过杀软的理论
1、静态特征分析
静态分析是基于黑名单的方法。当一个新的恶意软件被AV分析者检测到,特征码被提取。特征码是基于特殊的代码和数据(例如一个使用特殊字符串名的互斥量)。很多时候特征码使用恶意的可执行二进制文件的开始字节。AV拥有成千上万的恶意软件的特征码数据库,在这个恶意数据库中与扫描码进行匹配。第一代AV使用这个方法,现在依然在被使用,同时结合了启发式与动态分析。YARA工具可以被很容易用于创建规则并分类和识别恶意软件。这些规则被上传到AV和逆向工具。YARA 可以在
http://plusvic.github.io/yara/找到。
基于这种分析方法最大的问题是其不能够被用于检测新的恶意软件。这样为了绕过基于特征码的分析,一个人必须简单创建一个新的代码,或者做一些细微精确的修改抹去实际存在的特征。当在一个解密Sub中寻找指定的指令时,仍然可能在一个已经加密的恶意软件的代码中创建一个特征码。
2、静态启发式分析
在这种检测方式下,AV会检查已经在恶意软件中发现的代码模式。有大量可使用的依赖于AV供应商的规则。这些规则一般没有被描述(我想应该是为了避免它们被轻易绕过),这也是为什么总是不容易理解AV是如何判断一个软件是恶意的了。启发式分析的主要好处是,它能够检测到在数据库种并不存在其特征码的新的恶意代码。主要的缺点是,它会产生误 。
有一个例子:函数CallNextHookEx一般被用户态的键盘记录器使用。一些杀软认为这个函数的用法是一个威胁,如果这个函数的名字在可执行文件中被检测到,将发出一个关于这个软件启发式的警告。
另一个例子:一段代码打开“explorer.exe”进程,尝试写一些代码到其虚拟内存空间,这也被考虑为恶意的行为。
最容易的绕过启发式分析的方法是,确保所有的恶意代码是隐藏的。对于这个,编写解密的代码是最常用的方法。如果在解密之前,没有触发警告,如果这个解密Stub在解密完没有产生一些一般被认为恶意的行为,那么这个恶意软件不会被检测出来。
我基于Bill Blunden的书《Rootkit Arsenel》写了一段demo代码。这个代码是可用的,在
http://www.sevagas.com/?Code-segment-encryption,这里有另外一个链接,可以使Meterpreter的可执行程序对于杀软来说是检测不出来的。
(http://www.sevagas.com/?Hide-meterpreter-shellcode-in)。
3、动态分析
现如今,大部分的AV依赖于动态检测的方法。当一个可执行文件是被扫描,它会被运行在一个虚拟的环境很短的时间。结合特征码和启发式分析,可以检测到未知的恶意软件,甚至依赖于加密的恶意软件。确实,在AV沙盒中,代码是自加密的;接着,对于解密出来的代码能够触发一些可疑的行为。
如果一段代码使用加解密Stub隐藏一个恶意软件,倘若他们跳过解密阶段,大部分的AV会检测到它。这就意味着,绕过杀软动态分析依赖于两个方面:
有一个不被检测到的自解密机制(针对于启发式);
阻止AV执行解密stub;
我找出了有大量容易的方法来愚弄AV不执行解密Stub。
4、杀软的局限性
实际上,动态分析是一件复杂的事情,会扫描成千上万的文件,在仿真的环境执行它们,检查所有的特征码,但是它也有很多的局限性。动态分析模型有三个主要的局限性可以被利用:
环境是模拟的,意识不到机器和恶意软件环境的差异性。
仿真/沙盒系统有一些可以被恶意软件检测出来的差异性。
三、测试条件
1、本地环境
我编译了源代码并在本地搭建安装了杀软的Windows 7虚拟机环境下测试了这些代码。
2、VirusTotal
VirusTotal(https://www.virustotal.com)是针对多个AV的在线扫描的参考平台。它旨在为每个人提供验证可疑文件的可能性。它连接到超过50 个AV扫描器,囊括了所有主要的产品。VirusTotal也是检查AV绕过的一个有趣的技术平台。注意:VirusTotal不应该用于与实际安装的AV软件进行比较,因为它们有不同的版本和配置。此外,VirusTotal调用的AV服务可能与PC上安装的AV服务不同。
3、加密的恶意软件
对于我的测试,我利用在3.3中描述的方法。我需要一段一般会被认为是恶意代码的代码。最好的方法是使用已经众所周知的Merterpreter的payload。我创建了一段C代码,调用没有加密的Meterpreter shellcode,正如在这里有描述:
http://www.sevagas.com/?Hide-meterpreter-shellcode-in
我采用这样的方式加密了这段代码,这样AV静态分析就失败了(包括解密Stub的分析)。这个显示如今,AV依靠越来越多的动态分析,但是还不是主要使用它们。
四、复杂的方法
有一些被用于绕过杀软的复杂方法,这些方法很好的被文档化了,了解它们是很重要的,但这并不是这篇文章的主题(我们的主题是绕过杀软的简单方法 )。这些复杂的方法不仅仅是被用于绕过杀软的检测,还经常被现代的杀软使用。这两种复杂的方法在这里暗示使用了一种不同寻常的方法来运行代码。
1、代码注入方法
代码注入指在另一个进程内运行代码。这个一般通过DLL注入来实现,但也有其他可能存在的方法,甚至可能直接注入完整的exe(正如在这里描述的:
http://www.sevagas.com/?PE-injection-explained)。
这个过程的复杂性在于被注入的代码必须找到一种不被系统正常加载(尤其因为基地址不是一样的)也能执行自身的方法。对于DLL注入,当DLL被加载,其代码便会执行。对于代码注入,代码必须要基于其重定位表来修改其内存指针,重建IAT也是很重要的。
2、RunPE 方法
这个“RunPE”术语指出了一种方法,这个方法是通过替换掉进程空间的代码从而在目标进程中运行我们想要运行的代码。和代码注入不同的是,在代码注入中你是在远程进程开辟的空间中执行代码;但是在RunPE这个技术中,你使用你想要执行的代码替换掉了远程进程的代码。
下面是一个小的例子,说明这个方法是如何隐藏恶意代码的。想象一下,恶意代码被加壳或者加密了,被插入到一个专门加载它的二进制代码中。当加载器执行,它将执行:
在一个可疑的环境下使用CreateProcess打开一个合法的系统进程(例如:cmd.exe或者calc.exe)。
取消映射(Unmap)进程(使用NtUnmapViewOfSection)。
使用恶意代码替换掉这个进程(使用WriteProcessMemory)。
一旦进程被Resume(使用ResumeThread),恶意软件就会被替代进程执行。
注意:当进程被DEP保护的时候,替换一个进程的内存不是很有可能的,参考:
http://windows.microsoft.com/en-gb/windows-vista/what-is-data-execution-prevention。无论如何,在这种情况下,在另一个进程中使用RunPE,加载器能够调用其另外一个实例,并在其内存运行恶意代码。由于被修改的代码是被攻击者编写的,这个方法将总是有效(加载器应该在编译选项没有使用DEP的情况下编译,这个需要在这个测试中支持)。
RunPE结合自定义的解密Stubs是经常被自己要求是“FUD cryptor”(这个是可以在恶意软件市场得到的)的使用。正如代码注入的方法一样,但是因为这篇文章的主题不是这方面的,所以没有给充分的代码。
五、简单但有效的方法
如今,我们已经提出了一些复杂的方法,让我们继续来看看一些简单的方法,包括一些我测试过的代码例子。我也列出了每一个例子在VirusTotal 站的检测结果。
1、The “Offer you have to refuse” method
AV扫描器主要的限制是需要在每个文件上花费大量的时间。在一个常规的系统扫描中,AV必须要分析成百上千的文件。它不能够花费过多的时间和力量在个别的文件上(这就可以在AV上导致一个拒绝服务攻击)。最简单绕过AV的方法是仅仅在代码解密之前,消耗掉AV足够的时间。一个简单的Sleep不能够实现这个技巧,AV模拟器已经适应了这个。无论如何有大量的方法可以实现取得时间。这个被叫做“Offer you have to refuse”,因为它强行让AV去检查一些代码,这个会消耗掉AV大量的资源,因此我们确信在解密代码被执行之前AV会放弃这个检查。
例1:分配并填充100M内存
在第一个例子中,我们仅仅分配并填充100m内存。这个足以阻止任何模拟AV输出。
注意:在下面的代码中,大部分的AV会在malloc的过程中仅仅停止,关于分配指针的条件验证甚至没有必要。
VirusTotal检测结果:0/55
又是一个FUD方法。这个“Offer you have to refuse”是一个绕过杀软强大的方法。
2、The “I shouldn’t be able to do that!”
在这里的内容是,由于在一个仿真的系统里被推出,或许会有一些错误发生,代码或许在正常权限下不会运行。一般来说,在所有权限下,代码都将运行。这个能够被用来猜出这个代码是否在被分析。
例子1:尝试打开系统进程
代码会尝试打开一般是拥有所有权限的4 系统进程。如果这个代码没有运行在系统MIC和会话0下,这个将会失败(OpenProcess会返回00)。在这个VirusTotal中,你会看到这个不是FUD方法,而是绕过一些对这个特殊问题脆弱的杀软。
VirusTotal检测结果:11/55
However not the same AV as in §4.3, in fact only a couple detect the meterpreter part. All the other trigger the OpenProcess code as a malicious backdoor (static heuristic analysis). The point here is to show emulated environment does not behave the same as normal (malicious code are emulated with high privilege in AV).This could be adapted without triggering heuristic detection for example if malicious code is supposed to start without admin privileges.
然而与4.3部分的反病毒软件不同,实际上只有两个AV检测到了meterpreter部分。所有其他AV 毒是因为使用OpenProcess函数触发了杀软,被判定为恶意后门(静态启发式分析)。这里的要点是显示仿真环境与正常环境不同(恶意代码在AV中被以高权限模拟运行)。这可以在不触发启发式检测的情况下进行调整,例如:如果恶意代码本身是在没有管理员权限下运行的。
例子2: 尝试打开一个不存在URL
想自我意识到代码处于沙箱的一个方法是在互联 上下载一个指定的文件,然后与代码中已经知道的hash值进行比较。为什么这个有效呢?这是因为沙箱环境不给潜在的恶意代码访问互联 的权限。当一个被沙箱分析的文件访问互联 ,沙箱只会发送其自身成的文件。因此代码可以通过比较这个文件和其所期望的文件进来判断是否处于沙箱之中。
这个方法有一点问题,首先,如果你没有互联 的访问权限这个就没有效。其次,如果下载的文件改变或者被移除了,代码也不会有效。
另一个没有这些问题的方法是做这个相反的!尝试访问不存在的web域。在真实的世界里,这个会失败。在AV中,因为AV会使用其模拟的页面,这个方法是有效的。
VirusTotal检测结果:2/55
有意思的是,在这两个结果中,有一个AV认为我的stub或许是一个dropper(愚蠢的启发式误 )。第二个真正的找到了Meterpreter后门。这个的确很怪异,这就意味着,要不然是这些家伙有一个真正智能的系统,或者就是在它们使用的沙箱中允许 络连接。
我曾经读过一些文章,文章中说他上传到VirusTotal时,获得了一个Meterpreter的连接。
3、The “Knowing your enemy” method
如果某些人知道了一些目标机器上的一些信息,绕过杀软会变得相当的容易。把代码解密机制链接到你知道目标计算机上的一些信息(或者工作组)。
例子1: 依赖于本地用户名的操作
如果系统上的用户名被知道,可能做一些依赖于用户名的操作。例如,我们能够尝试访问在用户账户里面的文件。在下面的代码中我们在桌面创建了一个文件,我们在里面写了一些字符,然后只有我们可以打开这个文件,读取这些字符,我们开始解密方案。
VirusTotal检测结果:0/55
不用说这个就是FUD了。实际上,AV扫描器一般会在不是预期路径下创建和写入一个文件失败。我起初是很惊讶的,因为我预期AV是已经适应了主机PC,但它却不是这样的(我曾经在相同PC上测试过几个AV,不是仅仅使用了VirusTotal)。
4、The “WTF is that?” method
Windows系统API是如此的大以至于AV仿真系统没有覆盖到所有的东西。在这一节中,我放了两个例子而不是大量在Windows系统上API的漫谈。
例子1: What the fuck is NUMA?
NUMA代表Non Uniform Memory Access(非一致内存访问)。它是一个在多系统中配置内存管理的方法。它与定义在Kernel32.dll中所有一系列函数连接在一起。更多的信息可以参考这里:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363804(v=vs.85).aspx
下面的代码在常规的PC下是有效的,但是在AV仿真下会失败。
VirusTotal检测结果:0/55
例子2: What the fuck are FLS?
FLS是Fiber Local Storage(纤程本地存储),被用来操纵与纤程相关的数据。纤程是一整套运行在线程里的可执行组件。参考这里:
http://msdn.microsoft.com/en-gb/library/windows/desktop/ms682661(v=vs.85).aspx
有趣的是,一些AV仿真器对于FlsAlloc函数总是返回FLS_OUT_OF_INDEXES。
VirusTotal检测结果:8/55
5、The “Checking the environment” method
这里的主题也是简单的。如果AV依赖于一个沙盒/仿真环境,一般其环境与真实的环境是不一样的。有大量的方法做这种检查。下面描述了其中两种方法:
例子1:检查进程内存
使用Sysinternals工具,我意识到当一个AV扫描一个进程的时候,会改变它的内存。AV会为进程开辟内存,仿真的代码进程API也会返回与我们预期不同的值。在这种情况下,我在当前进程使用GetProcessMemoryInfo。如果当前工作设置大于3500000 bytes,我认为这个代码运行在一个AV环境中,如果不是,那么这个代码就解密并运行。
VirusTotal检测结果:1/55
几乎FUD了。似乎AV没有检测到Meterpreter,而是在主函数上触发了启发式检测。检测事件似乎是被链接到了被杀软pached的Windows系统可执行文件(别问我,在这个case中,为什么这个代码是被认为pached了的Windows系统可执行文件。)
例子2: Time distortion
我们知道Sleep函数是被AV仿真了。做这个是为了阻止使用一个简单的Sleep调用就绕过扫描时间限制。这个问题是,是否在这个模拟的Sleep方式中存在缺陷?
VirusTotal检测结果:8/55
明显一些AV在这个技巧的使用上失败了。
例子3: What is my name?
由于代码是被仿真的,它不是被启动为一个与二进制文件名字一样的进程。这个方法在这里有描述描述:http://blog.deepsec.net/?p=1613
测试的二进制文件时“test.exe”。在这个代码中,我们检查了包含这个文件名字的第一个参数。
VirusTotal检测结果:0/55
The DeepSec article was written in 2013 and method is still FUD
6、The “I call myself” method
这个与环境检查的方法不同。如果它以一种确定的方法调用,AV将仅仅触发这个代码。
例子1: I am my own father
在这个例子中,如果它的父进程也是test.exe的话,可执行文件(test.exe)才会进入解密的分支。当代码被安装,它会获取其父进程的ID,如果其父进程不是test.exe,它会调用test.exe然后停止。被调用的进程也有一个叫test.xee的父进程并且进入解密部分。
VirusTotal检测结果:1/55
AV一般不会跟踪这一类进程,因为它们会扫描父进程,而不是子进程(甚至实际上它是一样的代码)。
例子2: First open a mutex
在这个例子中,只有当一个确定的互斥量对象已经存在于系统中,代码(test.exe)才会开始解密代码。这个技巧是这样,当这个对象不存在,代码会创建并调用其自己一个新的实例。在父进程结束之前,子进程会尝试创建一个互斥量,会进入这个
ERROR_ALREADY_EXIST代码分支。
VirusTotal检测结果:0/55
这是另一个使用简单的方法实现的FUD效果。
六、结论
以上例子说明,若是能够利用杀软的弱点,绕过他们是很容易的。仅仅需要一些关于windows系统的知识和杀软工作的机制。但是,我并不是说杀软是没用的。杀软在检测已经存在于特征数据库种的恶意代码是非常有用的。同时,杀软对于系统恢复也是很有用的。我想说的是,杀软可以容易被新的病毒戏弄,尤其是对于有目的的攻击。
自定义的恶意软件经常作为APT攻击的一部分,杀软可能对于它们的攻击显的没有用。这并不意味着丢失了一切!对于杀软有选择的方案:加固系统、设置应用程序白名单机制、基于主机的入侵防御系统等。这些解决方案有其长度和短处。
如果我给一些谦虚的建议来抵抗恶意软件,我想说:
(1)没必要的情况下永远不要作为administrator权限去运行程序。这个黄金定律在没有杀软的情况下,能够避免99%的恶意软件。这个已经成为Linux用户做一些操作的正常的方式很多年了。这是我最重要的安全措施建议。
(2)加固系统,当前版本的windows系统有很强大的安全特性,尽管使用。
(3)部署NIDS(入侵检测系统)监控你的 络。很多时候,感染恶意软件并不是在受害者机器上被检测到的,而是应该感谢NIDS和防火墙日志。
(4)使用多个不同厂商的杀软。一个产品的长处可以覆盖另一个短处,也有可能一个国家的杀软对于来自该国家的杀软竟会更加熟悉。
(5)最后一点,安全意识建设。如果人被利用了,那么杀软基本是没用的。
参考文献:
https://wikileaks.org/ciav7p1/cms/files/BypassAVDynamics.pdf
作为国内领先的互联 内容安全和行为审计与监管整体解决方案提供商,多年来任子行一直致力于 络信息安全建设,构建基于风险监测的 络安全防护理念,实现对信息数据的有效管控和安全防护,可有力确保您的 络组织与信息数据安全。未来,任子行将致力于“绿色、高效、安全”的 络技术和产品的创新研究,以打造最具竞争力的互联 内容安全和行为审计与监管的民族品牌,为 络信息安全保驾护航。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!