一次前端RSA加密之使用Mitmdump自动化加解密渗透测试
字数 1087 2025-08-23 18:31:09
前端RSA加密之使用Mitmdump自动化加解密渗透测试
1. 概述
本文详细记录了在一次渗透测试中遇到前端RSA加密时的完整解决方案,包括JS逆向分析、加密算法破解、代码提取以及使用Mitmdump实现自动化加解密的完整流程。
2. 加密机制分析
2.1 加密流程
-
随机值生成:系统调用
y(32)函数生成两个32位随机值n和o- 基于字符串"0123456789ABCDEFabcdef"生成
-
数据加密:
d1:使用n作为key和o作为iv值,对原始报文进行AES加密(CBC模式)d2:使用服务端公钥和随机值n生成的RSA加密值d3:使用服务端公钥和随机值o生成的RSA加密值
-
请求结构:最终请求体为
{"d1":"...","d2":"...","d3":"..."}格式
2.2 解密流程
- 响应体解密:
- 使用客户端私钥解密
d2得到n - 使用客户端私钥解密
d3得到o - 使用
n和o解密d1得到原始报文
- 使用客户端私钥解密
3. JS逆向过程
3.1 定位加密函数
- 在浏览器控制台搜索
"d1:"定位加密代码位置 - 通过断点调试分析加密流程:
- 在
a的生成处下断点 - 单步调试进入
Object(v["f"])函数 - 确认使用了CBC模式的AES加密
- 在
3.2 关键变量分析
e:AES key值(来自n)i:AES iv值(来自o)t:加密前的请求体值r:RSA公钥值
3.3 Webpack代码提取技巧
- 定位入口函数:在HTML中搜索
call关键字 - 通用提取模板:
var test_module;
(function(e) {
var u={};
function f(c) {
if (u[c]) return u[c].exports;
var n = u[c] = {
i: c,
l: !1,
exports: {}
};
return e[c].call(n.exports, n, n.exports, f),
n.l = !0,
n.exports
}
test_module=f;
})({
// 提取的加密函数代码
})
- 加密函数提取:找到AES加密函数所在模块,提取完整代码
"4b2a": function(t, e, i) {
"use strict";
i.d(e, "a", (function() { return n })),
i.d(e, "c", (function() { return o })),
i.d(e, "b", (function() { return a })),
i.d(e, "d", (function() { return u })),
i.d(e, "f", (function() { return c })),
i.d(e, "e", (function() { return f }));
i("d3b7");
var r = i("7c74").sm2;
function n() {
var t = r.generateKeyPairHex();
return new Promise((function(e) { t && e(t) }))
}
function o(t, e) {
var i = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : 1,
n = "04" + r.doEncrypt(t, e, i);
return n
}
function a(t, e) {
var i = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : 1,
n = r.doDecrypt(t.substring(2), e, i);
return n
}
var s = i("7c74").sm3;
function u(t) {
var e = s(t);
return e
}
var h = i("7c74").sm4;
function c(t, e, i) {
var r = h.encrypt(t, e, { mode: "cbc", iv: i });
return r
}
function f(t, e, i) {
var r = h.decrypt(t, e, { mode: "cbc", iv: i });
return r
}
}
- 调用方法:
function test_enc(t, e, i) {
var test=test_module("7a74");
var r = test.encrypt(t, e, { mode: "cbc", iv: i });
return r
}
4. Mitmdump自动化加解密实现
4.1 Python脚本模板
import execjs
from mitmproxy import flowfilter
from mitmproxy.http import HTTPFlow
import subprocess
import json
from functools import partial
# 解决中文编码问题
subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')
# 加载JS代码
with open("test.js","r",encoding="utf-8") as f:
js_code=f.read()
f.close()
ctx=execjs.compile(js_code)
# 配置密钥
publickey="替换为公钥"
privatekey="替换为私钥"
def enc(data):
[d1,d2,d3]=ctx.call("req_enc",data,publickey)
return [d1,d2,d3]
def dec(d1,d2,d3):
result=ctx.call("res_dec",d1,d2,d3,privatekey)
return result
class FilterFlow:
def request(self, flow):
if flow.request.url.startswith("https://目标地址/"):
req=flow.request.get_text()
if '"d1":' in req: return
print("加密数据前:".format(req))
data=enc(req)
d1,d2,d3 = data[0],data[1],data[2]
json_data={"d1":d1,"d2":d2,"d3":d3}
result=json.dumps(json_data)
print("加密数据后:".format(result))
flow.request.set_text(result)
def response(self, flow:HTTPFlow):
if flow.request.url.startswith("https://目标地址/"):
resp=flow.response.get_text()
data=json.loads(resp)
d1,d2,d3 = data["d1"],data["d2"],data["d3"]
result=dec(d1,d2,d3)
flow.response.set_text(result)
addons = [FilterFlow(),]
4.2 代理配置方案
方案一:仅响应体解密(Burp上游代理)
mitmdump -p 9090 -s ./test.py --ssl-insecure
Burp设置:
- User Options > Upstream Proxy Servers > Add
- 配置目标域名为9090端口
方案二:完整加解密(Burp上下游代理)
- 上游代理:
mitmdump -p 9090 -s ./test.py --ssl-insecure
- 下游代理:
mitmdump -p 7070 -s ./test.py --mode upstream:https://127.0.0.1:8080 --ssl-insecure
- 浏览器配置代理为Mitm的7070端口
4.3 与其他工具集成
使用SQLMap等工具时,将流量代理至Burp:
python sqlmap.py -u "https://目标地址/?id=1" --batch --proxy=http://127.0.0.1:8080
5. 应用场景
该方法适用于以下场景(需能获取加解密或防篡改的源代码):
- Web网页端做了防篡改及加解密
- App端做了防篡改及加解密
- 小程序端做了防篡改及加解密
6. 参考资源
- 利用mitmproxy+burpsuite实现请求和响应加解密
- Webpack逆向分析技巧
- SM2/SM3/SM4国密算法文档