【验证码逆向专栏】某亭雷池 waf 验证码逆向分析
字数 1171 2025-08-29 22:41:38
某亭雷池WAF验证码逆向分析教学文档
一、目标分析
目标网站:aHR0cHM6Ly95dW5wYW4xLndhbmcv (已脱敏)
验证码类型:无感验证、直滑验证
二、环境检测绕过
1. 控制台检测机制
当打开开发者工具时,页面显示"当前环境正在被调试",这是通过v2/challenge.js文件中的控制台检测实现的。
2. 检测点定位
搜索关键字devtools发现关键检测函数:
devtoolsFormatterChecker- 控制台格式检查器_detectLoop- 轮询检测函数,每500ms检查一次控制台状态
3. 绕过方法
方法一:注释或替换检查器
// 将检查器return f.a注释或替换
方法二:重写轮询函数
// 断点定位到_detectLoop调用处
this._detectLoop = function() {}; // 直接置空
方法三:修改launch方法调用
参考项目:https://github.com/AEPKILL/devtools-detector
三、接口分析
1. 请求流程
- 首次请求首页,状态码468
- 验证成功后,cookie携带
sl-challenge-jwt值 - 成功返回数据,状态码200
2. 关键接口
issue接口:
client_id:首次请求首页468响应内容中获取
verify接口:
visitorId:访问者ID(需分析)issue_id:由issue接口返回result:加密参数(需分析)serials:轨迹值(无感/直滑验证的区别点)
四、参数逆向
1. visitorId生成
定位到v2/challenge.js文件,使用fingerprintjs v3库生成设备指纹:
const FingerprintJS = require('@fingerprintjs/fingerprintjs');
FingerprintJS.load().then(fp => {
fp.get().then(result => {
console.log(result.visitorId);
});
});
替代方案:使用fingerprintjs2(对Node.js环境更友好)
const Fingerprint2 = require('fingerprintjs2');
Fingerprint2.get(components => {
const values = components.map(component => component.value);
const murmur = Fingerprint2.x64hash128(values.join(''), 31);
console.log(murmur);
});
2. result参数加密
加密流程分析
- 通过XHR断点定位到result生成位置
- 发现是通过
e(t.data)的返回值 - 使用
createObjectURL创建临时对象链接
HOOK定位方法
originalCreateObjectURL = URL.createObjectURL;
URL.createObjectURL = function(obj) {
console.trace("Blob 被创建了", obj);
debugger;
return originalCreateObjectURL.call(this, obj);
};
加密实现
- 使用
v2/calc.wasm文件进行WebAssembly计算 - 传入
issue接口返回的data值 - 通过WASM模块处理后返回result
Node.js复现代码:
const fs = require('fs');
function get_result(data){
wasm = fs.readFileSync('./calc.wasm');
e = {
data: {
"action": "calc",
"data": {
"data": data,
"issue_id": ""
},
"wasm": wasm
}
}
var result;
var flag = false;
WebAssembly.instantiate(e.data.wasm).then(function(t) {
result = function(e) {
return t.instance.exports.reset(),
e.map(function(e) {
return t.instance.exports.arg(e)
}),
Array(t.instance.exports.calc()).fill(-1).map(function() {
return t.instance.exports.ret()
})
}(e.data.data.data);
flag = true;
}).catch(function(e) {
console.log(e);
})
while (!flag) {
require('deasync').sleep(100);
}
return result;
};
console.log(get_result([99, 35, 92, 48, 61, 31, 18, 43, 3, 54, 48, 22, 62, 50]));
// 输出: [ 17, 26, 51, 20 ]
兼容算法
分析发现还有一套JS算法与WASM计算结果一致:
function f(e) {
for (var t = 1, n = e.reduce(function(e, t) {
return e + t
}, 0), r = (6 + e.length + n) % 6 + 6; r--; )
t *= 6;
t < 6666 && (t *= e.length),
t > 0x3f940aa && (t = Math.floor(t / e.length));
for (var o = 0; o < e.length; o++)
t += Math.pow(e[o], 3),
t ^= o,
t ^= e[o] + o;
for (var f = []; t > 0; )
f.unshift(63 & t),
t >>= 6;
return f
};
console.log(f([99, 35, 92, 48, 61, 31, 18, 43, 3, 54, 48, 22, 62, 50]));
// 输出: [ 17, 26, 51, 20 ]
五、完整验证流程
- 首次请求获取
client_id - 生成
visitorId - 调用issue接口获取
data和issue_id - 使用WASM或JS算法计算
result - 构造verify请求参数
- 获取
sl-challenge-jwt并携带访问
六、注意事项
- 本文所有技术仅用于学习交流
- 严禁用于商业用途和非法用途
- 已对敏感信息进行脱敏处理
- 未经许可禁止转载或二次传播