从javascript脚本混淆说起

2017 年 7 月 17 日 FreeBuf 渔村安全

* 本文作者:渔村安全,本文属FreeBuf原创奖励计划,未经许可禁止转载

脚本病毒是一个一直以来就存在,且长期活跃着的一种与PE病毒完全不同的一类病毒类型,其制作的门槛低、混淆加密方式的千变万化,容易传播、容易躲避检测,不为广大网民熟知等诸多特性,都深深吸引着各色各样的恶意软件制作者 …

小到一个不起眼的lnk快捷方式,大到一个word文档,都是脚本的载体。本文主要以 js脚本为例(特指JScript,下同 ),具体结合实际样本,讲述混淆方式及其混淆类型检测相关知识,文章受限于样本个数及其种类,存在一定的局限性,但大致情况应当不会相差太大。本系列首先会对jscript及其脚本进行简单介绍,之后对采用不同混淆操作的样本进行分析以及总结,后续系列会引入脚本动态鉴定技术即 虚拟执行行为检测技术的介绍与实现。

前言

jscript是由微软公司开发的脚本语言,是对ECMAScript语法规范的实现,最初是随着 IE 3.0于1996年8月发布,和其所开发的其他脚本语言一样,后来被 Windows ScriptHost(WSH)和Active Server Pages所支持,其典型的扩展名称为.js 。Windows上的.js文件运行是这样的( 实际后缀还有可能是.jse,.wsf, .wsh),在你双击这类文件时,通常的操作是由代理程序传给宿主进程作为参数运行,这个宿主进程也通常为 wscript.exe或者cscript.exe(实际根据注册表中 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts.js\OpenWithList中给定的值)。 wscript与cscript的区别就在于前者是在窗口中运行,后者是在命令行中运行。那么是不是所有js 的脚本必须要有后缀?当然不是,你可以使用wscript //e:jscript 文件名称的形式执行。是不是还要问 jscript和javascript是不是同一个?当然不是! 但他们确实属于同一类语言,因为都是对ECMAScript语法规范的不同实现版本,大体语法都一样,但各自的实现却也存在着很多差异,具体差异可在网上查阅资料进行了解。

正文

jscript不知是什么时候被微软开始边缘化的技术产品(或许是javascript 太强了),随着老一批的微软开发者的淡出,jscript也离开了人们的视线。但也可能正是如此,它获得了恶意软件编写者的青睐。

什么是混淆

混淆:指迷惑,将一样东西误认为另一样东西(本解释来自百度百科 )。由于脚本语言绝大多数是基于解释器的运行方式,所以其在进入解释器之前多为源代码形式存在,通过源代码可以清楚知道脚本的意图,所以为了对抗分析,采用了各种方式将源代码进行 混淆操作(当然也可能是正常功能的脚本不想被轻易看穿的不得已之为)。通过金山毒霸安全实验室对全球非 PE样本的监控数据来看,目前对jscript脚本的混淆方式变化如下:

下面通过不同混淆操作的特点,结合实际样本案例进行分析总结,最后给出针对不同混淆方式的检测思路。

脚本混淆

脚本混淆的方式比较繁多,常采用编码、加密、 变形,切分等操作进行

编码: 将信息从一种形式或格式转换为另一种形式的过程

加密: 通过某种特殊算法,改变信息的原有结构

变形: 改变原来形态的过程

切分: 通常为拆分操作

上述操作是常见混淆的采用的技术操作,但实际上,对这些操作运用的载体,通常是变量 ,字符串,函数(或方法,统称函数)、数组、对象, eval等。下文针对这些不同的载体,具体结合样本进行分析讨论。

1. 变量的混淆

变量混淆,通常针对变量的名称、个数进行操作,目的是去掉变量名称的词义。这种操作的极端情况有个数极少、个数极多、名称长度普遍较短(<6)、名称长度普遍较长(>41) 、随机名称等特性。通常,单一的针对变量的混淆都是配合其他形式同时出现的,例如:

这个脚本的特点很明显,就是变量名称都是e构成的,而且超级多,其后续执行的代码如下:

具体含义就是根据变量名称中e的长度从低到高的顺序,通过this 访问当前环境变量,之后拼接完整代码块,使用eval进行动态执行。通过这个规律可轻易写出代码还原工具(但可能适用的范围很小 )。针对变量的混淆检测,主要从变量的个数 、名称长度分布及名称字符串特征这三个维度去检测。上述例子中,很明显的发现,这里对代码进行了 变形,代码块切分技术。

2. 字符串操作的混淆

字符串,很大程度上能够帮助代码阅读人员快速定位关键代码段,从而加快分析。当然,恶意脚本也会更加注意对字符串的保护 。对字符串的常见混淆操作有加密、编码、替换等操作。常见的针对字符串的操作函数有substr、substring、 unescape、fromcharcode、replace 、split以及正则表达式等操作。如下代码片段就是使用了字符串解密函数 exq在运行时对字符串进行了解密。

当然,也有如下这种通过字符串常用操作进行字符串解密的

实际运行后b的值是

还有比较常见的编码方式的

通过收集到的样本来看,其中使用正则表达式的样本也是比较多的,限于篇幅长度,这里就不一一列举了。

针对字符串混淆,大多数都会有字符串操作函数(或正则对象 )的使用,通过对这些函数(对象)的追踪,统计其出现的次数,能从一方面说明这类混淆的特性,当然如果不对语法进行深层次的分析,很容易将 循环中的操作个数统计为一次,而实际可能执行了好多次操作。

3. 函数、数组、对象的混淆

正常脚本中函数、数组、对象的个数都是有一个上限的,但混淆后的脚本往往会超过这个上限,从而变的异常。例如:一个脚本中出现了1000次函数声明以及函数表达式操作,一个数组的索引超过了 10000,以及一个对象中都是number:string。针对数组的混淆,看这个例子,

其中BOql这个数组,最大索引竟然达到19027,人工编写的代码一般来说是不能达到如此程度的,所以极有可能是通过代码生成的。实际运行后 eval传入的UTf代码片段如下:

当然,对函数的混淆也是比较多的,比如直接作为数组中的元素

或是返回对象中的某个元素

总体来讲,函数的混淆通常结合对象与数组同时进行。对于对象来说,比较意外,这种混淆通常是针对number,string 进行,很少发现里面有函数的出现,比如下面这个

同函数一样,对象的混淆大多数也是与数组一同出现的,核心原理是代码切分,这类混淆后的代码很明显的体现了这个概念 ,尽可能多的减少信息聚合,当然这也构成了这类混淆的检测特征索引过多但实际内容很少。

4. eval**的混淆**

eval函数可以动态执行一段代码,基于这个特性,可以说,很受恶意脚本的喜爱。常常是拼接代码段 + eval的方式执行完整脚本,也会被用来执行某一段代码。比较典型的 packed混淆属于第一种情况,如下:

实际执行的代码在p变量中。也有执行某一段代码的

eval的这类混淆,并不能作为一种独立特征来评判某脚本是否被混淆,通常是作为一种辅助特征而存在。

5. 其他类型混淆

上文讲了4种常见混淆方式,当然,很多特殊脚本,会使用一些其他技巧绕过检测,例如:直接使用 eval可能会被检测,那么转而使用this[e + val ]也是能达到相同特点的,这是this的一种特性,能访问当前环境的全部变量。或者,如下代码可能会被检测到存在风险

var sh = WScript.CreateObject("wscript.shell" )
sh.run('cmd')

但通过如下混淆

var o = this['e' + 'val']( 'th'+'is')
var sh = o['WS'+ 'cript']['Create' + 'Ob' + 'ject']("wsc" + "ript" + "." + "shELl" )
sh['R'+'un' ]('C' + 'm' + 'd' )

是不是很难被检测到了?当然,没有绝对检测不到的混淆,但是一定程度上,这种已经不能轻易从静态(代码执行之前)特征上进行判定,关于这些的检测判定会在本系列的第二篇中进行介绍。

环境检测

一个脚本的行为特性,有时会受到当前运行环境影响,比如说,去下载一个文件,但链接文件不存在,通常做法是直接提示然后结束,又或者说,去创建一个文件,之后去判断是否创建成功,成功之后做剩余操作,失败则直接提示后结束。所以总体来讲,脚本的行为会受到环境的影响。所以,恶意软件也会去尝试进行环境判断,从而判定当前的环境是否正常,例如是否允许在查杀引擎 中,下面列出了几种常见的对抗检测技术。

1. 时间检测

在一般情况下,查杀引擎对一个脚本的扫描时长都会限定在一个范围之内,沙盒也是如此。恶意脚本会通过延迟执行的方式,从使检测程序超时退出,达到对抗的效果,例如:

这段代码出现在脚本的最前端,保证在延迟大于6s后继续运行。

2. 执行结果检测

现在对严重混淆脚本的检测大多是基于虚拟执行技术(详见本系列的第二篇 ),由于引擎自身对实际环境的虚拟程度有限,往往存在某些对象、函数的模拟不到位,导致在恶意脚本执行时触发异常(或直接崩溃) ,从而发生流程转移,例如:

实际xuzydfej9函数执行的代码如下:

var luzpal9 = WScript.CreateObject(‘WScript.Shell’);

var pniwinosu8 = luzpal9.CreateShortcut(‘\qvertyd.lnk’);

if(pniwinosu8.TargetPath == ‘’) return 1;

通过探测是否成功创建lnk文件来判断是否处于真实环境,与这种类似,还有一些是通过设置时间来进行探测,总之这类统称为执行结果检测。

3. 其他

/@cc_on @/形式的条件编译,只有支持jscript语法的宿主中才会执行,所以如果某 虚拟执行引擎不支持这类语法规则的话,则会直接以/**/注释的形式跳过这段代码,从而对脚本实际行为出现错觉 。

最后

本文是<从jscript脚本混淆说起 >的系列文章第一篇,主要目的是以科普为主,简单介绍jscript的背景、常见混淆方式以及环境检测相关的内容。通过这些的介绍,希望能让大家对jscript 恶意脚本,非PE病毒目前的发展趋势有一个初步了解,本系列的后续文章将继续深入探讨通过虚拟执行技术来检测恶意脚本。最后,感谢阅读本文。

* 本文作者:渔村安全,本文属FreeBuf原创奖励计划,未经许可禁止转载

登录查看更多
0

相关内容

JavaScript 是弱类型的动态脚本语言,支持多种编程范式,包括面向对象和函数式编程。
【2020新书】实战R语言4,323页pdf
专知会员服务
100+阅读 · 2020年7月1日
【实用书】Python爬虫Web抓取数据,第二版,306页pdf
专知会员服务
117+阅读 · 2020年5月10日
【经典书】精通机器学习特征工程,中文版,178页pdf
专知会员服务
354+阅读 · 2020年2月15日
【书籍推荐】简洁的Python编程(Clean Python),附274页pdf
专知会员服务
179+阅读 · 2020年1月1日
【电子书】C++ Primer Plus 第6版,附PDF
专知会员服务
87+阅读 · 2019年11月25日
从webview到flutter:详解iOS中的Web开发
前端之巅
5+阅读 · 2019年3月24日
基于Web页面验证码机制漏洞的检测
FreeBuf
7+阅读 · 2019年3月15日
如何编写完美的 Python 命令行程序?
CSDN
5+阅读 · 2019年1月19日
逆向 | C++ 加壳程序的编写思路
计算机与网络安全
9+阅读 · 2019年1月1日
如何用GitLab本地私有化部署代码库?
Python程序员
9+阅读 · 2018年12月29日
Python | Jupyter导出PDF,自定义脚本告别G安装包
程序人生
7+阅读 · 2018年7月17日
Python 杠上 Java、C/C++,赢面有几成?
CSDN
6+阅读 · 2018年4月12日
Python为啥这么牛?
Python程序员
3+阅读 · 2018年3月30日
33款可用来抓数据的开源爬虫软件工具 (推荐收藏)
数据科学浅谈
7+阅读 · 2017年7月29日
Arxiv
5+阅读 · 2019年10月11日
Arxiv
7+阅读 · 2018年3月19日
Arxiv
7+阅读 · 2017年12月28日
Arxiv
3+阅读 · 2017年11月21日
VIP会员
相关资讯
从webview到flutter:详解iOS中的Web开发
前端之巅
5+阅读 · 2019年3月24日
基于Web页面验证码机制漏洞的检测
FreeBuf
7+阅读 · 2019年3月15日
如何编写完美的 Python 命令行程序?
CSDN
5+阅读 · 2019年1月19日
逆向 | C++ 加壳程序的编写思路
计算机与网络安全
9+阅读 · 2019年1月1日
如何用GitLab本地私有化部署代码库?
Python程序员
9+阅读 · 2018年12月29日
Python | Jupyter导出PDF,自定义脚本告别G安装包
程序人生
7+阅读 · 2018年7月17日
Python 杠上 Java、C/C++,赢面有几成?
CSDN
6+阅读 · 2018年4月12日
Python为啥这么牛?
Python程序员
3+阅读 · 2018年3月30日
33款可用来抓数据的开源爬虫软件工具 (推荐收藏)
数据科学浅谈
7+阅读 · 2017年7月29日
Top
微信扫码咨询专知VIP会员