【古法渗透】 jsrpc mitmproxy联动burp实现加密自动化
原创
暗月大徒弟
暗月大徒弟
moonsec
2026年8月4日 12:12
广东
在小说阅读器读本章
去阅读
免责声明:本公众号所提供的文字和信息仅供学习和研究使用,不得用于任何非法用途。我们强烈谴责任何非法活动,并严格遵守法律法规。读者应该自觉遵守法律法规,不得利用本公众号所提供的信息从事任何违法活动。本公众号不对读者的任何违法行为承担任何责任。
一、介绍
JsRpc 是黑脸怪开源、Go 后端 + 前端 JS 注入、基于 WebSocket 双向长连接 的 RPC 远程调用工具,核心定位:JS 逆向免补环境神器,项目地址:https://github.com/weixinpad/JsRpc
二、运行原理
浏览器页面注入一段 JsRpc 环境 JS,主动通过 WebSocket 连接本地 Go 服务端;你在 Python/HTTP 接口下发指令,让目标网页原生浏览器环境执行 JS 代码 / 加密函数,结果回传给本地,彻底不用在 Node.js 里模拟 DOM、BOM、浏览器内置 API 补环境。
三、整体结构
- Server 服务端:Go 语言编写,监听 HTTP + WebSocket,默认端口 12080
- Browser 客户端:浏览器页面注入 JsEnv\_De.js 脚本(JS 客户端)
- Caller 调用端:Python /curl/ Burp / 自己写的程序,通过 HTTP 发指令
核心通信链路:调用端 → HTTP → Go 服务 → WebSocket 长连接 → 浏览器执行 JS → 原路返回结果
四、jsrpc实战操作
下载js前端加密对抗练习靶场 https://github.com/SwagXz/encrypt-labs 在本地进行搭建
使用phpstudy 搭建 网站 修改database.php数据库配置信息 导入sql导入数据库 访问即可
账号和密码 admin/123456
加密分析
登录之后 选择 aes固定key
查看网络发现是一段密文
js分析
搜寻关键词 encrypt 查看代码 js代码都是经混饶 断点调试
查看整个加密方式 使用CryptoJS库对进行aes进行加密 固定的iv和key都是 1234567890123456
const _0x67b862 = CryptoJS['enc']['Utf8']['parse']('1234567890123456')
, _0x2d9cd5 = CryptoJS['enc'][_0x2fe90c(0x118, 0x11a)]['parse']('1234567890123456')
, _0x1375d7 = CryptoJS['AES'][_0x2fe90c(0x119, 0x11c)](_0x807d91, _0x67b862, {
'iv': _0x2d9cd5,
'mode': CryptoJS[_0x2fe90c(0x11a, 0x11b)]['CBC'],
'padding': CryptoJS[_0x2fe90c(0x11b, 0x11e)][_0x2fe90c(0x11c, 0x12c)]
})[_0x2fe90c(0x11d, 0x10e)]()
, _0x550d63 = 'encryptedData=' + encodeURIComponent(_0x1375d7);
主要的作用对传入的字符串字符串进行aes加密接着url编码返回赋值
这是没有经过混饶的代码
function encryptData(data) {
const key = CryptoJS.enc.Utf8.parse('1234567890123456');
const iv = CryptoJS.enc.Utf8.parse('1234567890123456');
return CryptoJS.AES.encrypt(data, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).toString();
}
使用jsrcp调用该混饶代码
在jsrcp目录中运行该window\_amd64.exe服务器端
在目录中 resouces\JsEnv\_Dev.js 赋值该代码到js终端中执行
var rpc_client_id, HlClient = function (wsURL) {
this.wsURL = wsURL;
this.handlers = {
_execjs: function (resolve, param) {
try {
var fn = new Function('return (async () => { return (' + param + ') })()');
var result = fn();
if (result && typeof result.then === 'function') {
result.then(function(res) {
resolve(res !== undefined ? res : \"执行成功(无返回值)\");
}).catch(function(err) {
resolve(\"执行错误: \" + (err.message || err));
});
} else {
resolve(result !== undefined ? result : \"执行成功(无返回值)\");
}
} catch (err) {
resolve(\"语法错误: \" + (err.message || err));
}
}
};
this.socket = undefined;
if (!wsURL) {
throw new Error('wsURL can not be empty!!')
}
this.connect()
}
HlClient.prototype.connect = function () {
if (this.wsURL.indexOf(\"clientId=\") === -1 && rpc_client_id) {
this.wsURL += \"&clientId=\" + rpc_client_id
}
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._reportActions();
});
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;
this._reportActions();
return true
}
HlClient.prototype._reportActions = function () {
var actions = Object.keys(this.handlers);
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.send(JSON.stringify({
\"action\": \"_registerActions\",
\"message_id\": \"\",
\"response_data\": JSON.stringify(actions)
}));
}
}
HlClient.prototype.handlerRequest = function (requestJson) {
var _this = this;
try {
var result = JSON.parse(requestJson)
} catch (error) {
console.log(\"请求信息解析错误\", requestJson);
return
}
if (result[\"registerId\"]) {
rpc_client_id = result['registerId']
return
}
if (!result['action'] || !result[\"message_id\"]) {
console.warn('没有方法或者消息id,不处理');
return
}
var action = result[\"action\"], message_id = result[\"message_id\"]
var theHandler = this.handlers[action];
if (!theHandler) {
this.sendResult(action, message_id, 'action没找到');
return
}
try {
if (!result[\"param\"]) {
const async_result = theHandler(function (response) {
_this.sendResult(action, message_id, response);
})
if (async_result && typeof async_result.then === \"function\") {
async_result.catch(e => {
_this.sendResult(action, message_id, \"\" + e);
});
}
return
}
var param = result[\"param\"]
try {
param = JSON.parse(param)
} catch (e) {
}
theHandler(function (response) {
_this.sendResult(action, message_id, response);
}, param)
} catch (e) {
console.log(\"error: \" + e);
_this.sendResult(action, message_id, \"\" + e);
}
}
HlClient.prototype.sendResult = function (action, message_id, e) {
if (typeof e === 'object' && e !== null) {
try {
e = JSON.stringify(e)
} catch (v) {
console.log(v)
}
}
this.send(JSON.stringify({\"action\": action, \"message_id\": message_id, \"response_data\": e}));
}
如图
注入环境之后连接通信 grup是组 一般不用设置使用默认的就可以了
var demo = new HlClient(\"ws://127.0.0.1:12080/ws?group=zzz\");
在服务端上回显信息表示连接成功。
动态注册函数
在js中选择替换修改js内容 选择文件夹
修改后文件进行保存 ctrl+f5进行刷新
const _0x67b862 = CryptoJS['enc']['Utf8']['parse']('1234567890123456')
const _0x2d9cd5 = CryptoJS['enc'][_0x2fe90c(0x118, 0x11a)]['parse']('1234567890123456')
demo.regAction(\"encryptest\", function (resolve,param) {
const _0x807d91 = JSON['stringify'](param);
const _0x1375d7 = CryptoJS['AES'][_0x2fe90c(0x119, 0x11c)](_0x807d91, _0x67b862, {
'iv': _0x2d9cd5,
'mode': CryptoJS[_0x2fe90c(0x11a, 0x11b)]['CBC'],
'padding': CryptoJS[_0x2fe90c(0x11b, 0x11e)][_0x2fe90c(0x11c, 0x12c)]
})[_0x2fe90c(0x11d, 0x10e)]()
const _0x550d63 = 'encryptedData=' + encodeURIComponent(_0x1375d7);
resolve(_0x550d63);
});
encryptest 是注册的函数名 resolve是返回的内容 param是传入的参数
再进行调用
http://127.0.0.1:12080/go?group=zzz&action=encryptest¶m={\"username\":\"admin\",\"password\":\"123456\"}
python编写调用脚本
encrypt\_demo.py
import json
import requests
def encrypt(data):
url = \"http://127.0.0.1:12080/go\"
params = {
\"group\": \"zzz\",
\"action\": \"encryptest\",
\"param\": data
}
response = requests.get(url, params=params)
context = response.json()['data']
return context
data={\"username\":\"admin\",\"password\":\"123456\"}
body_data = json.dumps(data)
print(encrypt(body_data))
执行返回 返回加密的字符串
五、mitmproxy联动 burp调用jsrpc
mitmproxy 是一个“中间人代理”。它位于你的客户端(如浏览器、手机应用程序)和目标服务器之间,拦截、检查、和重放所有流经的 HTTP/HTTPS 请求和响应。
将mitmproxy作为burp的上游代理,当请求包经过mitmproxy时,通过python脚本处理请求体(&searchCond之前的部分)后传给jsrpc进行加密,mitmproxy将请求体中的sign替换后发送给服务端,流程图如下。
安装
pip install mitmproxy
mitmproxy 是可以加载python脚本处理数据包
import json
from urllib.parse import parse_qs
from mitmproxy import http
import requests
def encrypt(data):
url = \"http://127.0.0.1:12080/go\"
params = {
\"group\": \"zzz\",
\"action\": \"encryptest\",
\"param\": data
}
response = requests.get(url, params=params)
context = response.json()['data']
return context
class Demo:
def request(self, flow: http.HTTPFlow) -> None:
url = \"/encrypt/aes.php\"
if flow.request.method == \"POST\" and flow.request.path == url:
if \"encryptedData\" in flow.request.text:
# 解析POST表单请求体,转成字典
data = parse_qs(flow.request.text)
encrypt_data = data.get(\"encryptedData\", [''])[0]
new_data = encrypt(encrypt_data)
flow.request.text = new_data
addons = [Demo()]
使用命令
mitmproxy -p 端口 -s 指定脚本
执行命令
mitmproxy -p 8899 -s C:\\Users\\moon\\PycharmProjects\\PythonProject\\encrypt_burp.py
运行没有出错 在burp中设置上游代理
接着提交数据包 返回true
接着可以测试穷举
参考 https://www.cnblogs.com/xiaoqiuni/p/18860405
需要本文相关工具
关注公众号回复【20260804】
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论