实战webpack_js算法逆向分析

记录学习前端JS逆向算法。

遇到一个网站,使用了webpack

test

输入账号密码后,发现密码被加密了。
test

此时尝试定位该加密算法,并正向爆破。

当前登录的请求接口是/prod-api/auth/login
test

此时全局搜索该接口

成功找到该接口文件

test

打开该js文件,通过分析js函数,可知2727行r函数作用是登录请求,2719行o函数作用是加密。

test

此时设置断点,观察数值变化

test

r函数中的t是用户名admine是明文密码123456

此时下一步

test

密码e通过2728行的函数调用,传递到了加密函数o

加密函数o中,t是明文密码,e是密钥,密钥是2021123456789012

加密后123456的密文是wzGWkli7/KSZszgljZhEng==

test

通过分析发现,此处的加解密算法的对象名是a,通过调用对象a下的属性,实现的加密。

test

将这段加密的函数,复制到我们的js文件中

test

此时尝试去找到a在哪声明的。

通过分析,发现a2717行被声明

test

并且对象a的值,是由s调用了n中的3452模块。

此时我们要找到n,看n是在哪出现的,我们并把它给拿出来。

test

因此设置断点直接跟过去。

此时找到了n的声明。

test

我们将这段声明n函数的代码复制到js文件中,即把分发器复制到js代码中。

并在外部声明一个用于接收分发器值的变量

通过定位追踪

test

test

我们将o值,赋值给用户接收分发器函数返回值的变量。

test

即在分发器代码的末尾处进行赋值。

test

此时分发器确定了,这个时候要找分发器调用了哪些webpack的模块。

通过前面的分析可知,分发器调用的第一个webpack的模块是3452

test

此时全局搜索3452是在哪个js文件下。

test

此时找到了3452模块,而3452模块又调用了大量的其他模块,因此我们要一一定位。

test

通过分析后发现,所有的模块和3452模块都是处于同一个js文件中的。

test

此时创建一个modules.js文件,用于管理这些模块。
将该存放模块的js代码,全部复制粘贴到本地的modules.js文件中。

并设置全局变量进行调用
var window = global;

test

并在encrypt.js文件中导入该模块js文件。

并调用加密函数,查看加密结果。

test

执行代码后,报错,提示n未被定义。

test

报错的原因是因为,此时分发器的模块调用的结果,并未赋值给n,而是我最开始设置的变量flag,此flag接收了分发器的返回结果。因此我们要把n改成flag

修改后,再次运行

test

123456在本地加密的结果和在前端加密的结果值是一样的。

![test][link26]

此时尝试重复并多次加密明文。

使用nodejs,按行读写passwords.txt

并将密码的密文并输出到encrypt_passwors.txt中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const readline = require('readline');
const fs = require('fs');

( () => {
let fRead = fs.createReadStream("passwords.txt");
let objReadLine = readline.createInterface({
input: fRead
});
objReadLine.on('line', function (line) {
console.log(encrypt(line));
fs.writeFile('./encrypt_passwors.txt', encrypt(line)+'\n', { 'flag': 'a' }, function(err) {
if (err) {
throw err;
}
});
});
})();

test

实际上此处的加解密就是一个AES算法ECB模式的加解密。
用python很容易就能实现加解密。
但是我主要是想通过模块化的方式的调用,来实现加解密。

test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import base64
from Crypto.Cipher import AES
#解密
def aes_decode(data, key):
try:
aes = AES.new(str.encode(key), AES.MODE_ECB) # 初始化加密器
decrypted_text = aes.decrypt(base64.decodebytes(bytes(data, encoding='utf8'))).decode("utf8") # 解密
decrypted_text = decrypted_text[:-ord(decrypted_text[-1])] # 去除多余补位
except Exception as e:
pass
return decrypted_text

#加密
def aes_encode(data, key):
while len(data) % 16 != 0: # 补足字符串长度为16的倍数
data += (16 - len(data) % 16) * chr(16 - len(data) % 16)
data = str.encode(data)
aes = AES.new(str.encode(key), AES.MODE_ECB) # 初始化加密器
return str(base64.encodebytes(aes.encrypt(data)), encoding='utf8').replace('\n', '') # 加密

if __name__ == '__main__':
key = '2021123456789012' #密钥
data = "A!d@m#i$n@123" #明文数据

mi = aes_encode(data,key)
print("加密值:",mi)
print("解密值:",aes_decode(mi,key))

尝试去使用webpack的模块解密aes的加密密文,找了半天也没找到调用了哪些webpack的模块,因此解密失败了,太fw了。

Author: jdr
Link: https://jdr2021.github.io/2021/10/15/实战webpack-js算法逆向分析/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.