前言
今天遇到了一个需求,想要抓取 fofa 引擎 “相关搜索” 的数据:
看了一下请求包,发现参数带有签名校验,无奈之下只能逆向分析 Js ,找到参数加密的方式:
分析过程
首先查看请求包的调用栈:
本着见名知意的原则,注意到 getRelevantSearch
这个函数,于是跟进:
发现这里已经是执行请求了,但是注意到,这里使用的是对象 a
的 get
方法来实现的请求,应该是进行了自定义的功能实现,所以搜索对象 a
,找到封装的方法:
简单分析下这里的代码,找到签名 sign
生成的部分:
if ("get" === t.method) {
t.params || (t.params = {}),
(t.params.ts = Date.now()),
"en" === c.$cookies.get("i18n_redirected") && (t.params.lang = "en");
var g = JSON.parse(JSON.stringify(t.params)) || {};
(t.params.sign = c.$sortFun(g, f) || ""),
(t.params.app_id = "9e9fb94330d97833acfbc041ee1a76793f1bc691");
}
核心在于: t.params.sign = c.$sortFun(g, f) || ""
,在这里打印一下 c.$sortFun
,就能找到对应的函数是什么:
注入代码之后,刷新页面触发事件:
跟进到这个函数:
是一个独立的函数,所以直接提取出来:
var sortFun = function (e) {
var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "",
n = {},
i = {};
for (var r in e) String(e[r]) && (n[r] = e[r]);
Object.keys(n)
.sort()
.forEach(function (e) {
i[e] = n[e];
});
var a = "";
Object.keys(i).forEach(function (e) {
a += "".concat(e).concat(i[e]);
});
var o = crypto.createSign("RSA-SHA256");
return o.update(a), o.sign(t, "base64");
};
注意:函数中的 Na.createSign("RSA-SHA256")
实际上就是 crypoto
的创建签名函数,所以可以直接替换。
解决 sortFun
这个函数之后,再回到调用它的地方,接着解决其他参数的问题:
t.params.sign = c.$sortFun(g, f) || ""
这里传入两个参数: g
和 f
,其中, g
比较直接,就是 t.params
,直接打印就可以看到结构:
然后就是参数 f
,定义在上面:
注意:
a.a
实际上就是crypto
;e
实际上就是buffer.Buffer
;
所以可以稍微修改,得到代码如下:
var n, i, r, o, s, l, p, f =
((n =
"d28041345794095f0d48bb623a1d357d:3e8665669760a71b573f178fd823586ab140f5859499bf41216442b3816ca52c9031bd60dc8315277e352ecf81f964836a30da4c6b9adf32db3d7c955bbd043e6102ba82dfb2799dee81737b405fa6bab61c24ec65af65d9446a9f1f987f1b6ccb0a32678fc69c07c49262410e682e3fc862fcaa176eda49b89ff4b23d244e10e808c17dde61cfcfa99c513934f4ebc03c6a5ae23b48358dadb1c210b6e5a38fbaa25bf1898a7107e5010c52f980ab851bcd3799a0b125da5934c80ac5ed3ef67474532bafa725a80283b9f3361641952dd0af0e0dd2d303d6fc55336f0621d22bddd0b6f210722a9f181d500c3033554dbf89fa264c808768963f0675a56e28efdcf0a027a05ecd27579d3675bd6e8f42c66dd175172a09f701fc3fd8a4f55fefa565d7b32f4b8953737646f9a758efa41640eb92b364757320960d8bbd22ebf00a07c3a245aef9ff2e001cff08b06670078454d9db4696e4c23de8f031b6e6bc1123f817bfaf108192f376a6248612625745d273cd3c2ef318f3aa67d3785fe973898f7ade355373e0afd226fabcc2c9851661c3c8e2be98e29b106adb16a61946064136f942494204d898bb1e87e8b4ecbc80fd87b8a38e7197b69e5e2d71a88b038ad56e9c19a223a40d1ef6363777d8cf11d2052dda4a5f4422365dd33f6493a1db4e59e96bc480f76f67ec20c49a169b9a1c603a2281afadb594efbef49608c7bc72074202a8fa8456f8408b3d02ff54ca94b1227b0ecae4cbe9ff4226aa0908f7c566fa212ce77c084f5010ebbcd337ead2b46a25d0943a2a5014e86c5ceaeaa5e62cf5f90878c5e69dfd4d74a6c28ba0a2933cd7856af0443e3dc44ab10cebe6d4324a2cf531f8011ea02cc6974f18780b9a679035cca7bba45eb3dea553484d14d7d39fbec3aa7ee33ed43674fd63578efb43fdd8eda181a2482f1b266c70fe5a61349626c9f09ff9e30790515b321b899802d2b925e2c131dd8afdacaa10ad6d64acf3d10e4ca69048fcc981d1723145734951c3441400c3c6b9e965f06b4b91eae261a6e53e621b8d4d7cd93b687432936c0c1c9c696bc6ad1658d00d8dc94a5a5e9cd60d3909abb78b43f0bd6baf9d84d1851f6ee1663ed788b9370cbf6032b994bf5ae7cd32d1e8fad9cd6f68dc80ef6b4fd8bee4129101e142d926177bc37486844215076e58e7da1edc8c6135c0ebcfee895182bd40a8acaae1c447bb1511f4567b149d19cff68614034d85f65d6c25d3c2015faf7d22008a21d8b8f2b73b8a01ff29fe99922f01f4837c581887b2de9250acc699fa3d61682d4df8ab1d6b1fba3982250ba29e3e0af4e0ab02e4cca01668c799b5ec872c0274b61d0a093cc61f18e9e390e8a4f22a0e79118b7335b27173ec0b52173ff3754672b28daf3520fcd651cf53da29c77b48ae352b1f65ec663f0e17e1ea2d7b2aa666593d6d64822fff397ea0ce7aae281f1ddcb19834cff32433100e35297e9ab06f0ddd104f8a59d54aa0d9b9779bfd089332991826d9c28c877b76720d8d188a2fac64afb74e87b49f22d780e15fd0098d933f75f4c5700fa497e0b32db4d618492d876baa7e37ab5293e0a7d80af417e813a10eeaa48b545cb6ed1832bb5073e35f9d669dd58319ed463cdf873cc391ad3828843607622cdf9a106ad23d8f0ddd8231af2d711c6111d2e43359548055f858a7ed8e43571a9b626deaf313d946f7df384794dbb233e942c6780027a84841fd93dee9e5be4cacefffc4bf80a5c540283d88900433fd0a2972438d456b89bd8814f9080f6b9c697854a64f1ac43e41c5d55b9baca6b106e37d315e235d52c4166731b3440fb8f7caced207cb05d0ad6563b9a00e062328767bb030af24da8083c85ac4dec3cfb2309fd1066343381732a6d2f129f8a5aaf538012d3144c15f0f907f7f8fbe4eb4aa2200ac29a88298adb3235af03c8e07de306c218eb7cae07a1c918e6a675740d7529285c42285c0c84d84e19a8dc5301d5cc67eb537de097b67befebaf6b63f27ac2333eb6b9a1e2aefe6f6a8c286093dbd1726e075d04d29562558adb3047283fdc39a60229511de4e4655a2614d3fbd6ef9d9c9b4bf3c344021b6de43c8274a3ecea187b5bc39a942a3254ff511354d98896a2fd4dcbe84eb36c14e369019c6a5db8e3b7fa63884da9a9b406d33ac6f9069c860a57c5288e6063eb9391b4ee4893272efe08b911a26a3c62502c25c33787e1b41b71b9e4cc910ff14bf40a02331d8e24e78980dd4125036173f0eff629418c0703fda2d8164ceeb6a12390d23e6ef4edbd3e4b9f13fbaaf9da0a4cd53067b7be07b1d95ab905cc7d274e4d0cf9b406cc8f66f3a142968d8d1c4502bc712f0f49696f49c9121edf61612583a895b8776bf6758c3ac59f1dc026216428db0f3daf609"),
(p = "fofa4.9.145"),
(i = crypto.createHash("sha256").update(p).digest()),
(r = n.split(":")),
(o = buffer.from(r.shift(), "hex")),
(s = buffer.from(r.join(":"), "hex")),
(l = crypto.createDecipheriv("aes-256-cbc", buffer.from(i), o)),
l.update(s, "hex", "utf8") + l.final("utf8"));
最后一个参数 app_id
也是代码中硬编码的,直接复制即可:
app_id = '9e9fb94330d97833acfbc041ee1a76793f1bc691'
到这里之后,发现签名过程并不复杂,仅仅使用了 keyword, page, size, timestamp
进行 sha-256 加密,密钥也可以直接获取,那么就可以开始组装代码了。
代码测试
完整代码如下:
const fs = require("fs");
const crypto = require("crypto");
const buffer = require("buffer").Buffer;
const appid = "9e9fb94330d97833acfbc041ee1a76793f1bc691";
var sortFunc = function (e) {
var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "",
n = {},
i = {};
for (var r in e) String(e[r]) && (n[r] = e[r]);
Object.keys(n)
.sort()
.forEach(function (e) {
i[e] = n[e];
});
var a = "";
Object.keys(i).forEach(function (e) {
a += "".concat(e).concat(i[e]);
});
var o = crypto.createSign("RSA-SHA256");
return o.update(a), o.sign(t, "base64");
};
var n,
i,
r,
o,
s,
l,
p,
f =
((n =
"d28041345794095f0d48bb623a1d357d:3e8665669760a71b573f178fd823586ab140f5859499bf41216442b3816ca52c9031bd60dc8315277e352ecf81f964836a30da4c6b9adf32db3d7c955bbd043e6102ba82dfb2799dee81737b405fa6bab61c24ec65af65d9446a9f1f987f1b6ccb0a32678fc69c07c49262410e682e3fc862fcaa176eda49b89ff4b23d244e10e808c17dde61cfcfa99c513934f4ebc03c6a5ae23b48358dadb1c210b6e5a38fbaa25bf1898a7107e5010c52f980ab851bcd3799a0b125da5934c80ac5ed3ef67474532bafa725a80283b9f3361641952dd0af0e0dd2d303d6fc55336f0621d22bddd0b6f210722a9f181d500c3033554dbf89fa264c808768963f0675a56e28efdcf0a027a05ecd27579d3675bd6e8f42c66dd175172a09f701fc3fd8a4f55fefa565d7b32f4b8953737646f9a758efa41640eb92b364757320960d8bbd22ebf00a07c3a245aef9ff2e001cff08b06670078454d9db4696e4c23de8f031b6e6bc1123f817bfaf108192f376a6248612625745d273cd3c2ef318f3aa67d3785fe973898f7ade355373e0afd226fabcc2c9851661c3c8e2be98e29b106adb16a61946064136f942494204d898bb1e87e8b4ecbc80fd87b8a38e7197b69e5e2d71a88b038ad56e9c19a223a40d1ef6363777d8cf11d2052dda4a5f4422365dd33f6493a1db4e59e96bc480f76f67ec20c49a169b9a1c603a2281afadb594efbef49608c7bc72074202a8fa8456f8408b3d02ff54ca94b1227b0ecae4cbe9ff4226aa0908f7c566fa212ce77c084f5010ebbcd337ead2b46a25d0943a2a5014e86c5ceaeaa5e62cf5f90878c5e69dfd4d74a6c28ba0a2933cd7856af0443e3dc44ab10cebe6d4324a2cf531f8011ea02cc6974f18780b9a679035cca7bba45eb3dea553484d14d7d39fbec3aa7ee33ed43674fd63578efb43fdd8eda181a2482f1b266c70fe5a61349626c9f09ff9e30790515b321b899802d2b925e2c131dd8afdacaa10ad6d64acf3d10e4ca69048fcc981d1723145734951c3441400c3c6b9e965f06b4b91eae261a6e53e621b8d4d7cd93b687432936c0c1c9c696bc6ad1658d00d8dc94a5a5e9cd60d3909abb78b43f0bd6baf9d84d1851f6ee1663ed788b9370cbf6032b994bf5ae7cd32d1e8fad9cd6f68dc80ef6b4fd8bee4129101e142d926177bc37486844215076e58e7da1edc8c6135c0ebcfee895182bd40a8acaae1c447bb1511f4567b149d19cff68614034d85f65d6c25d3c2015faf7d22008a21d8b8f2b73b8a01ff29fe99922f01f4837c581887b2de9250acc699fa3d61682d4df8ab1d6b1fba3982250ba29e3e0af4e0ab02e4cca01668c799b5ec872c0274b61d0a093cc61f18e9e390e8a4f22a0e79118b7335b27173ec0b52173ff3754672b28daf3520fcd651cf53da29c77b48ae352b1f65ec663f0e17e1ea2d7b2aa666593d6d64822fff397ea0ce7aae281f1ddcb19834cff32433100e35297e9ab06f0ddd104f8a59d54aa0d9b9779bfd089332991826d9c28c877b76720d8d188a2fac64afb74e87b49f22d780e15fd0098d933f75f4c5700fa497e0b32db4d618492d876baa7e37ab5293e0a7d80af417e813a10eeaa48b545cb6ed1832bb5073e35f9d669dd58319ed463cdf873cc391ad3828843607622cdf9a106ad23d8f0ddd8231af2d711c6111d2e43359548055f858a7ed8e43571a9b626deaf313d946f7df384794dbb233e942c6780027a84841fd93dee9e5be4cacefffc4bf80a5c540283d88900433fd0a2972438d456b89bd8814f9080f6b9c697854a64f1ac43e41c5d55b9baca6b106e37d315e235d52c4166731b3440fb8f7caced207cb05d0ad6563b9a00e062328767bb030af24da8083c85ac4dec3cfb2309fd1066343381732a6d2f129f8a5aaf538012d3144c15f0f907f7f8fbe4eb4aa2200ac29a88298adb3235af03c8e07de306c218eb7cae07a1c918e6a675740d7529285c42285c0c84d84e19a8dc5301d5cc67eb537de097b67befebaf6b63f27ac2333eb6b9a1e2aefe6f6a8c286093dbd1726e075d04d29562558adb3047283fdc39a60229511de4e4655a2614d3fbd6ef9d9c9b4bf3c344021b6de43c8274a3ecea187b5bc39a942a3254ff511354d98896a2fd4dcbe84eb36c14e369019c6a5db8e3b7fa63884da9a9b406d33ac6f9069c860a57c5288e6063eb9391b4ee4893272efe08b911a26a3c62502c25c33787e1b41b71b9e4cc910ff14bf40a02331d8e24e78980dd4125036173f0eff629418c0703fda2d8164ceeb6a12390d23e6ef4edbd3e4b9f13fbaaf9da0a4cd53067b7be07b1d95ab905cc7d274e4d0cf9b406cc8f66f3a142968d8d1c4502bc712f0f49696f49c9121edf61612583a895b8776bf6758c3ac59f1dc026216428db0f3daf609"),
(p = "fofa4.9.145"),
(i = crypto.createHash("sha256").update(p).digest()),
(r = n.split(":")),
(o = buffer.from(r.shift(), "hex")),
(s = buffer.from(r.join(":"), "hex")),
(l = crypto.createDecipheriv("aes-256-cbc", buffer.from(i), o)),
l.update(s, "hex", "utf8") + l.final("utf8"));
var word = '"nginx"';
var time = Date.now();
var page = 1;
var size = 10;
var data = {
ts: time,
page: page,
size: size,
keyword: word,
};
var g = JSON.parse(JSON.stringify(data)) || {};
var s = sortFunc(g, f);
fetch(
`https://api.fofa.info/v1/search/rules?keyword=${encodeURI(word)}&page=${page}&size=${size}&ts=${time}&sign=${s}&app_id=${appid}`,
)
.then((resp) => {
return resp.json();
})
.then((json) => {
console.log(json["data"]["rules"]);
});
测试成功: