-
-
[原创]***登录接口 sign 签名 + 密码 RSA 加密逆向实战(JS 逆向 / MD5 签名 / JSEncrypt / execjs)
-
发表于: 2026-10-19 07:55 642
-
[原创]***登录接口 sign 签名 + 密码 RSA 加密逆向实战(JS 逆向 / MD5 签名 / JSEncrypt / execjs)
本文是「JS 逆向实战 30 例」系列第 9 篇,项目源码:JS 逆向实战 30 例 · 开源仓库 (Project9)
36Kr 的手机号密码登录,我想还原它的登录协议。
站点:f6aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1j5K6y4X3E0J5i4K6u0W2j5$3!0E0i4K6u0r3
目标接口:eecK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4j5i4c8W2N6$3q4&6i4K6u0W2x3K6k6C8M7W2)9J5k6h3y4G2L8g2)9J5c8X3q4H3K9g2)9J5c8X3#2#2M7#2)9J5c8X3I4G2k6$3W2F1i4K6u0r3j5Y4W2y4L8$3u0A6L8r3g2b7j5i4y4K6N6$3!0J5k6l9`.`.
需还原字段:sign、nonce、timestamp、mobileNo、password
难度:⭐⭐⭐⭐(四星。手机号 / 密码各自 RSA 加密,再加一层 MD5 签名,且签名依赖首页下发的动态 nonce)
这个项目有意思的地方在于:签名里混入了一个来自首页的随机串 nonce,也就是 window.__GATEWAY_SIGN__。只扣 JS 还不够,必须先请求一次首页把这个值取回来,才能算出正确的 sign。这在网关型 API(gateway)里非常常见。
打开 6ffK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1j5K6y4X3E0J5i4K6u0W2j5$3!0E0i4K6u0r3i4@1g2r3i4@1u0o6i4K6S2o6i4@1f1%4i4K6V1&6i4@1u0n7i4@1f1#2i4@1u0p5i4K6V1#2i4@1f1$3i4@1p5I4i4K6R3$3i4@1f1^5i4@1u0q4i4K6V1K6i4@1f1#2i4K6R3#2i4@1p5#2i4@1f1$3i4K6R3&6i4K6S2n7i4@1f1$3i4K6W2o6i4@1u0m8i4@1f1#2i4K6S2r3i4@1t1%4i4@1f1#2i4@1q4r3i4K6R3$3i4@1f1%4i4@1p5H3i4K6R3I4i4@1f1%4i4K6R3J5i4@1t1&6i4@1f1%4i4K6V1&6i4@1u0n7i4@1f1#2i4@1u0p5i4K6V1#2i4@1g2r3i4@1u0o6i4K6S2o6c8U0p5J5 → Network,抓 byMobilePassword 这个请求。
先看 Request URL 的 Query:
sign 挂在 query 上。
再看 Payload(请求体),是一个 JSON:
规律非常清晰:
mobileNo / password 都是 Base64 密文(RSA 加密后 Base64);
nonce 是一个 Base64 串;
timestamp 是毫秒时间戳;
sign 是 query 上的 MD5 串。
【图1:byMobilePassword 请求的 Query 里的 sign 与 Body 里的加密 param —— 建议截图】
还有一个关键线索:登录前页面里会有 window.__GATEWAY_SIGN__="...",它就是这里的 nonce。
登录是前端 JS 发出的,所以定位思路:
XHR 断点:在 Sources → XHR/Fetch Breakpoints 里加 byMobilePassword,触发登录,断下来后看调用栈;
往上层回溯:找到封装请求的函数后,往上看它怎么组装 param 和 sign;
同时全局搜 setPublicKey、JSEncrypt、encrypt、MD5、sign。
最终抠出来的核心函数 get_arg(来自真实文件 js/36kr_2.js):
拆解一下:
i 是站点写死的 RSA 公钥;
a(...) 是一个内部函数:用 BBB(491) 拿到 webpack 里的 JSEncrypt 模块,setPublicKey(i) 后 encrypt(),对手机号 / 密码分别加密(注意 RSA 加密是每次都带随机 padding 的,所以同样的手机号每次密文也不同);
md5 是文件顶部定义的 cryto_js.MD5(text).toString();
sign = md5(JSON串 + GATEWAY_SIGN) —— 这就是签名精髓:把登录 body 的 JSON 原样字符串再拼上首页下发的 nonce,一起 MD5。
文件顶部很简单,直接 require('crypto-js'):
【图2:XHR 断点命中后回溯到 get_arg 的调用栈 —— 建议截图】
这一篇的还原重点是两段环境的配合:
nonce 不从 JS 里算,而是从首页 HTML 里抠。仓库里 py/arg.py 专门干这件事:
做法就是 GET 首页,然后从 <script async>window.__GATEWAY_SIGN__="..." 里切出那串 Base64。注意 nonce 和 Cookie 是配套的,_c_WBKFRo 这类 WAF Cookie(占位符 你的_c_WBKFRo)如果失效,首页可能拿不到这个值。
把上一步的 nonce 作为 GATEWAY_SIGN 参数喂给 36kr_2.js 的 get_arg:
补环境只做了 window = global; self = window;
JSEncrypt 从 webpack 模块 BBB(491) 里取,所以我把整个 webpack 运行时保留,没有把那段 !function(e){...} 删掉;
md5 用 crypto-js,所以要 npm i crypto-js。
关于 RSA 加密,几个必须记住的点:
用 setPublicKey + encrypt,不是 encryptLong(这里手机号密码都不长);
每次 encrypt 结果不同(PKCS#1 v1.5 随机填充),所以 sign 里的密文和实际发出去的密文必须来自同一次调用,不能分开算两次。
主脚本 py/36kr.py 把“取 nonce → 生成签名参数 → 发登录请求”串起来:
结构非常干净:get_arg()(network)拿 nonce → Js.call(...) 拿签名参数 → sign 放 query、其余放 body。
⚠️ 小提醒:这里 Js.call(get_arg, GATEWAY_SIGN) 的第一个参数严格来说应该是函数名字符串("get_arg")。仓库里直接把 Python 的 get_arg 函数对象传了进去(因为 from arg import get_arg 同名),属于一个容易踩的写法坑,实际使用时建议写成 Js.call("get_arg", GATEWAY_SIGN),语义更明确。
执行 python 36kr.py,终端会打印登录接口的返回。如果账号密码正确、签名与 nonce 匹配,会返回类似:
如果 sign 不对,会返回签名校验失败的提示——这正好可以用来验证我们的签名算法是否还原正确。
【图3:终端打印登录接口返回 —— 建议截图】
nonce 和 Cookie 是配套的。 GATEWAY_SIGN 来自首页,首页又依赖 WAF Cookie(_c_WBKFRo 等占位符)。换一套 Cookie 后要重新请求首页拿新 nonce,否则签名必错。
sign 里的密文必须和发出去的密文一致。 RSA 每次加密都带随机填充,如果你为了打印调试而对密码调了两次 a("你的密码"),两次密文不同,签出来的 sign 就对不上。
sign 拼的是 body 的紧凑 JSON。 Arg 里字段顺序是 krtoken → partner_id → timestamp → param,顺序和空格都影响 MD5,必须和最终发出的 JSON 完全一致。Python 侧用了 json.dumps(..., separators=(',', ':')) 来保证紧凑格式,但字段顺序由 dict 决定,要保证和 JS 里拼的一样。
JSEncrypt 从 webpack 模块取。 别把 BBB(491) 那段 webpack 运行时删掉,否则找不到模块。
self = window 也要补。 有些打包代码会访问 self,Node 里没有,补上更稳。
crypto-js 依赖。 顶部 require('crypto-js') 需要 npm i crypto-js。
账号密码脱敏。 仓库里 mobileNo = a("你的手机号") / password = a("你的密码") 都是占位符,跑之前换成自己的可用测试账号。
赞赏
- [原创]荔枝网 X-ITOUCHTV 头签名 WASM 逆向实战(webpack + wasm-bindgen + sig.json 中转) 877
- [原创]scrape.center 靶场 WASM 签名 JS 逆向实战(WebAssembly.encrypt + Node 子进程 + Python) 529
- [原创]国家医保服务平台国密 SM2/SM4 签名 JS 逆向实战(x-tif 头 + 补环境 + execjs) 480
- [原创]凤凰云智登录密码 RSA 加密 JS 逆向实战(大整数 RSA + 阿里 IDaaS + execjs 补环境) 928
- [原创]**电子卖场 nsssjss/sign 请求头签名逆向实战(JS 逆向 / RSA / 嵌套哈希 / execjs) 523