JsRpc+Yakit热加载解决请求响应体加解密问题
字数 905 2025-09-01 11:26:11
JsRpc + Yakit 热加载解决请求响应体加解密问题
引言
本文介绍如何利用 JsRpc 与 Yakit 热加载技术解决 Web 应用测试中遇到的请求与响应双向加密问题。通过这种方法,测试人员可以彻底摆脱加解密的束缚,实现端到端的明文测试。
工具准备
- JsRpc 项目地址: https://github.com/jxhczhl/JsRpc
- Yakit 项目地址: https://yaklang.com/products/download_and_install/
操作步骤
1. 安装并启动 Yakit 靶场
首先需要准备测试环境,Yakit 自带靶场可用于模拟加密场景。
2. JsRpc 使用
注入 JS,构建通信环境
将 JsRpc 的 JsEnv_Dev.js 代码(https://github.com/jxhczhl/JsRpc/blob/main/resouces/JsEnv_Dev.js)复制到浏览器控制台执行,建立 WebSocket 通信环境。
远程调用加密函数
- 编写加密辅助函数:
window.myEncrypt = function(data) {
const key = CryptoJS.enc.Hex.parse("6190987ec48f0394752f09a81ee869a2");
const iv = CryptoJS.enc.Hex.parse("557784509b449b0d67287c1b53571e72");
const message = typeof data === 'object' ? JSON.stringify(data) : data;
const encryptedResultObject = CryptoJS.AES.encrypt(
message,
key,
{
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
return encryptedResultObject.toString();
};
- 注册加密接口:
var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=encrypt");
demo.regAction("encrypt", function(resolve, param) {
var params = atob(param);
esultObject = eval('(' + params + ')');
resolve(myEncrypt(esultObject));
});
- 测试加密接口:
http://127.0.0.1:12080/go?group=encrypt&action=encrypt¶m=e3VzZXJuYW1lOiAnYWRtaW4nLCBwYXNzd29yZDonYWRhc2Rmc2RmJ30=
远程调用解密函数
- 编写解密辅助函数:
window.get_decrypt = function(key, iv, message) {
const decrypted = CryptoJS.AES.decrypt(
message,
CryptoJS.enc.Hex.parse(key),
{
iv: CryptoJS.enc.Hex.parse(iv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
decryptedData = JSON.parse(decrypted.toString(CryptoJS.enc.Utf8));
return decryptedData;
};
- 注册解密接口:
var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=get_decrypt");
demo.regAction("get_decrypt", function(resolve, param) {
res = get_decrypt(param["key"], param["iv"], param["message"])
resolve(res);
});
- 测试解密接口(Python示例):
import requests
import json
url = "http://127.0.0.1:12080/go"
data = {
"group": "get_decrypt",
"action": "get_decrypt",
"param": json.dumps({
"key": "9ca83c435044b0750777c05dec8df6bd",
"iv": "fc973c1d2895d89b445989939381b18c",
"message": "gcCZ3CRVQa7C1ZSTH06+Rfjz7E8x63WKTexphNg9YKcBpcUhW7yRnpMw+115wDO8XEqM12ECpCiP+7vfQDjkj08osdjmZhBw0EDZOv2FEBo="
})
}
res = requests.post(url, data=data)
print(res.text)
3. Yakit 热加载脚本实现
加密请求热加载脚本
# port scan plugin
yakit.AutoInitYakit()
handleCheck = func(target, port, word) {
addr = str.HostPort(target, port)
isTls = str.IsTLSServer(addr)
packet = `
GET /go?group=encrypt&action=encrypt¶m={{params(word)}} HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Host: {{params(target)}}
`
rsp, req, _ = poc.HTTP(packet,
poc.params({"target": addr, "word": word}),
poc.https(isTls),
poc.redirectTimes(0),
)
println(req)
if len(rsp) > 0 {
body = poc.GetHTTPPacketBody(rsp)
printf("获取加密结果:\n" + string(body))
jsonObj = json.loads(string(body))
sign = jsonObj["data"]
return sign
} else {
println("失败")
}
return
}
func base_word(name, passwd) {
word = "{ username: '" + name + "', password: '" + passwd + "' }"
println(word)
return codec.EncodeBase64(word)
}
signRequest = result => {
pairs := result.SplitN("|", 2)
dump(pairs)
word = base_word(pairs[0], pairs[1])
sign = handleCheck("127.0.0.1", 12080, word)
return sign
}
解密响应热加载脚本
# port scan plugin
yakit.AutoInitYakit()
func get_decrypt(key, iv, message) {
rsp, req = poc.HTTP("POST /go HTTP/1.1\r\nHost: 127.0.0.1:12080",
poc.replaceAllPostParams({
"group": "get_decrypt",
"action": "get_decrypt",
"param": json.dumps({"key": key, "iv": iv, "message": message})
}))~
println(req)
println(rsp)
return rsp
}
func decrypt(rsp) {
if len(rsp) > 0 {
body = poc.GetHTTPPacketBody(rsp)
printf("获取加密结果:\n" + string(body))
jsonObj = json.loads(string(body))
key = jsonObj["key"]
iv = jsonObj["iv"]
message = jsonObj["message"]
println("\nkey: " + string(key) + "\niv: " + string(iv) + "\nmessage: " + string(message))
if key == nil && iv == nil && message == nil {
return rsp
} else {
return key, iv, message
}
} else {
return rsp
}
return
}
afterRequest = func(rsp) {
key, iv, message = decrypt(rsp)
println("\nkey: " + string(key) + "\niv: " + string(iv) + "\nmessage: " + string(message))
rsp = get_decrypt(key, iv, message)
return rsp
}
关键点总结
-
JsRpc 核心功能:
- 作为桥梁调用前端页面中的加解密函数
- 通过 WebSocket 实现浏览器与测试工具的通信
-
Yakit 热加载优势:
- 无需重启即可实时修改脚本
- 利用
afterRequest魔术方法实现响应自动解密 - 支持请求自动加密
-
加解密处理:
- 固定 key 和 iv 确保加密结果可复现
- 支持多参数传递处理复杂解密场景
- 自动处理 JSON 格式转换
-
调试技巧:
- 所有调试信息输出到控制台
- 分阶段验证各组件功能
- 使用 Yakit 自带靶场快速验证
参考资料
通过这套方案,测试人员可以完全在明文环境下进行测试工作,所有加解密过程由工具自动完成,极大提高了测试效率和准确性。