Python调用JS代码实现恶意样本分析
客户安全设备发现恶意HTML样本,代码内容进行了混淆,让我分析一下。正好面向需求学习,学习一下Python调用JS代码的方法,掌握了这个方法再分析恶意样本简直手拿把掐
Python 调用 JS 代码通常是使用第三方库,常用的库主要有以下这些:
- PyV8
- Js2Py
- PyExecJS
- PyminiRacer
- Selenium
- Pyppeteer
这些库各有各的优劣,很多文章都没有详细介绍,下面我会简单说明一下其中几个第三方库的特点,然后着重介绍使用 PyExecJS 调用 JS 的方法
PyV8
- V8 是谷歌开源的 JavaScript 引擎,被使用在了 Chrome 中
- PyV8 是 V8 引擎的一个 Python 层的包装,可以用来调用 V8 引擎执行 JS 代码
- 年久失修,最新版本是 2013 年更新的(https://pypi.org/project/PyV8/#history)
- 具有较高的执行速度和良好的兼容性
- 存在内存泄漏问题,不建议使用
Js2Py
- Js2Py 是一个纯 Python 实现的 JavaScript 解释器和翻译器,会将 JavaScript 代码转换为等效的 Python 代码
- 有较好的兼容性,并且可以用于在 Python中 执行复杂的 JavaScript 代码
- 性能比直接使用原生 JavaScript 引擎慢一些,存在一些 BUG
- 对于高度混淆的大型 JS 可能会转换失败
PyMinRacer
- 同样是 V8 引擎的包装,和 PyV8 的效果一样
- 一个继任 PyExecJS 和 PyramidV8 的库
- 具有较小的内存占用和较快的执行速度,适用于嵌入式环境和资源受限的场景
- 缺点是不支持所有的JavaScript特性
Selenium
- 用于自动化浏览器操作,可以模拟用户在浏览器中的行为
- 支持多种浏览器,如 Chrome、Firefox、Safari 等,并提供了丰富的 API 来控制浏览器的行为
- 需要安装和配置浏览器驱动程序
- 执行速度上可能比其他纯JavaScript执行库慢一些
Pyppeteer
- 基于 Chrome 浏览器的无头浏览器控制库,用于模拟和控制浏览器的行为
- 和 Selenium 类似,都是模拟浏览器行为
- 需要安装和配置Chrome浏览器,某些场景下使用复杂
PyExecJS
- 一个最开始诞生于 Ruby 中的库,后来被移植到了 Python 上
- 较新的文章一般都会说用它来执行 JS 代码
- 有多个引擎可选,但一般我们会选择使用 NodeJS 作为引擎来执行代码
- 具有高度灵活性和通用性,使用简单
首先安装需要使用的库
1 | pip install pyexecjs |
打开 python 终端,验证当前的 js 环境,能正常返回 Node.js
就说明环境没问题
1 | In [1]: import execjs |
使用 PyExecJS 库非常简单,只需要简单两步就行:
- 编译 JS 代码,通过
execjs.compile()
函数实现 - 调用并运行 JS 代码中的方法获取返回值,通过
call()
函数实现,传入方法名和方法参数做为函数参数值
示例代码如下:
1 | import execjs |
也可以封装成方法,在需要时调用即可
1 | import execjs |
分析的样本就是这次客户发给我的,代码混淆的方式很简单,也很好助于理解,用来练习 python 代码调用 JS 算是个不错的示例样本
快速识别
在进行样本分析时,我们需要样本做了哪些恶意行为,如果代码执行最后一条语句是执行恶意代码,其他都是在对代码进行反混淆。那么我们找到要执行恶意代码的位置,将其修改为输出要执行的代码,便可以快速解析恶意样本行为
重点是找到 js 代码中最后执行恶意代码的那条语句,然后注释这条并打印要执行的代码,便可以很简单的获取到样本真正要执行的恶意行为
一次值守安全设备检测到恶意 HTML 文件访问,文件执行 JS 代码如下:
1 | var sc = "/xkvod/", |
文件执行逻辑其实很简单,混淆后的代码存放于变量vc
,通过调用函数qc
对混淆后的代码进行反解析并执行。执行解析后的恶意代码的语句是 wc(vc);
,我们只需要注释这行然后添加一行代码return vc;
,再调用这函数便能获取这段 JS 代码真正想要执行的操作
通过下面的 python 代码直接调用 JS 函数,获取执行代码返回值
1 | import execjs |
获取返回值,在拼接变量,可以知道 JS 代码执行的是网页跳转操作
1 | window.location="/xkvod/39618.html?rodqze=xjf1x2" |
本想继续跟进网页跳转,由于客户只给了恶意样本,客户没给明确的 IP 地址,只能单单针对恶意样本进行分析。这段代码这么大费周章的通过混淆实现重定向,行为可疑,已经被我直接打入死刑,上报客户进行封禁和终端查杀
代码分析
首先明确混淆后的代码,以十六进制形式加特殊符号进行保储存,我们可以对函数 qc 进行分段分析来看他做了哪些操作
1 | vc = "\xf6V\xf2)\x88\xf6\xed\xe6\xbe\x8bS\xafW\xd4\x15I\xe9\x1e\xab3\x8f\x87 \x97\x01\x09R\nL\xbb\x1d B\xe47\x04\xc8\x09\xad}\xa2\xc2\x94\x07V\xac\xda\xbc{\xf1\x27H\x88"; |
第一步
函数首先执行下面这段代码,遍历恶意代码中的元素,charCodeAt()
函数用于返回指定位置的 unicode 值,\xf6
对应的 unicode 值是 246
,V
对应 unicode 值为 86
1 | for(wc = 0; wc < vc.length; wc++) |
执行后的数组 uc 的结果如下
1 | [246, 86, 242, 41, 136, 246, 237, 230, 190, 139, 83, 175, 87, 212, 21, 73, 233, 30, 171, 51, 143, 135, 32, 151, 1, 9, 82, 10, 76, 187, 29, 32, 66, 228, 55, 4, 200, 9, 173, 125, 162, 194, 148, 7, 86, 172, 218, 188, 123, 241, 39, 72, 136] |
第二步
然后执行下面这段代码,从第 4 位开始到第 52 位结束,循环执行下面四个操作:
- 取两组数组中保存的 unicode 值与
231
进行按位异或运算 - 将第一组计算后的值左移 2 位,第二组计算后的值右移 6 位
- 再将第一组计算后的值与
0xff
进行按位与运算 - 然后将第一组计算后的值与第二组值进行按位或运算
- 最后将这个值进行按位取反后,再与
0xff
进行按位与运算
1 | wc = 4; |
计算过程弯弯绕绕,进行了多次位运算,最后数组 uc 变成了下面这些值
1 | [246, 86, 242, 41, 67, 188, 216, 252, 155, 79, 46, 223, 62, 52, 53, 70, 200, 25, 207, 173, 95, 127, 225, 63, 101, 69, 42, 73, 82, 143, 21, 225, 106, 244, 189, 113, 68, 69, 215, 150, 235, 108, 51, 125, 58, 211, 12, 147, 142, 168, 253, 66, 136] |
第三步
执行如下代码,分别是从数组第 48 位到数组第 2 位,将当前 unicode 值与数组前一位 unicode 值相加后再与 0xff
进行按位与运算
1 | wc=48; |
计算后数组值再次改变
1 | [246, 86, 72, 27, 108, 255, 148, 212, 151, 234, 125, 13, 29, 114, 105, 123, 14, 225, 232, 124, 12, 222, 96, 32, 164, 170, 111, 115, 155, 225, 164, 246, 75, 94, 177, 46, 181, 137, 28, 109, 129, 87, 159, 176, 183, 13, 223, 159, 33, 168, 253, 66, 136] |
第四步
执行如下代码,从第 50 位开始到第 2 位结束,对数组内的数据再次进行位运算
1 | wc=50; |
计算后的值如下
1 | [246, 86, 72, 79, 69, 78, 86, 1, 15, 77, 78, 66, 64, 85, 223, 72, 78, 79, 28, 82, 66, 75, 10, 3, 18, 24, 23, 16, 160, 25, 15, 73, 85, 76, 77, 245, 30, 83, 78, 69, 80, 91, 33, 68, 28, 89, 75, 3, 10, 30, 85, 66, 136] |
第五步
最后一段代码,首先从第 1 位开始,遍历 uc 数组,执行如下操作:
- 先将数组值与 rc 进行比较,rc 值为调用代码解析函数 qc 时传入的值
- 利用
String.fromCharCode()
函数将数组 unicode 编码值转换为对应字符 - 将数组下标与 7 进行取余计算,如果不为 0 就保存字符,即数组为 7 的倍数时丢弃该值
- 使用 eval 函数将字符串 vc 值作为 JS 代码执行
1 | vc = ""; |
简单捋一遍,逻辑还是挺简单的,就是将字符串转 16 进制然后通过位运算进行代码混淆,简单枯燥。主要是这次借机了解下 python 调用 JS 代码就用现有的案例分析一下,分析到后面我都觉得无趣了
总的来讲,分析安全设备捕获的恶意 HTML 恶意样本,如果代码有混淆,直接看调用样本的 JS 函数,获取函数最后执行的代码内容就完事了
复杂度高一些的使用 python 进行 JS 逆向的方法,在后续项目遇到后再详细讲述