Burp插件实现验证码接口行为测绘与爆破
1. 这不是“自动打码”,而是一次精准的验证码接口行为测绘
你有没有遇到过这样的情况:在渗透测试中,刚摸到一个登录接口,正准备上Burp Intruder跑字典,页面却弹出一个看似简单的四位数字验证码图片——刷新一次变一次,参数名还叫captcha,后端校验逻辑藏在某个不透明的/api/verify里。你本能地想点开开发者工具看请求,却发现验证码图片是base64内嵌的,校验请求里带着一个captcha_token字段,而这个token既不在图片响应头里,也不在HTML源码中明文出现。这时候,多数人会卡住:是写个Python脚本去OCR?还是找打码平台API对接?又或者干脆放弃,转去挖别的点?
我最近在复现Pikachu靶场的“验证码绕过”模块时,就撞上了这个典型场景。但这次我没走OCR或打码平台的老路,而是用了一个被很多人忽略的轻量级Burp插件——captcha-killer-modified。它名字里带“killer”,但实际干的活远比“暴力破解”更精细:它不识别字符,不调用AI模型,而是通过动态Hook前端JS生成逻辑 + 实时捕获token流转路径 + 自动化重放校验请求三步,把整个验证码的“生成-传输-校验”链路像X光一样照透。它解决的从来不是“怎么识别图片”,而是“为什么这个请求总失败”。关键词:Burp插件、captcha-killer-modified、Pikachu靶场、验证码接口爆破、前端JS Hook、token动态捕获。这篇文章就是一次完整的实战复盘,从环境确认、插件加载、JS逻辑逆向,到最终实现Intruder联动爆破的全过程。它适合所有正在学Web渗透、对验证码绕过感到模糊、或者已经用过原版captcha-killer但始终没跑通的人——因为真正卡住你的,往往不是插件本身,而是你没看清它依赖的那条隐性链路。
2. 为什么必须用modified版?原版在Pikachu上根本跑不通
先说结论:原版captcha-killer(v1.0.0)在Pikachu靶场完全失效,不是配置问题,是设计层面的兼容断层。这不是我试错出来的,而是通过对比两版插件源码和Pikachu前端JS行为后确认的硬伤。要理解这点,得先拆解Pikachu验证码模块的真实工作流:
Pikachu的验证码不是服务端直出图片+独立校验接口,而是典型的“前端驱动型”实现:
- 页面加载时,前端JS执行
get_captcha()函数,向/api/captcha发起GET请求; - 该接口返回JSON:
{"status":1,"data":{"img":"data:image/png;base64,...","token":"abc123"}}; - JS将base64字符串渲染为
<img>标签,同时把token存入全局变量window.captchaToken; - 用户输入后点击登录,JS读取
window.captchaToken拼接到POST数据中,发往/api/login。
关键来了:原版captcha-killer的设计预设是“验证码图片URL可直接访问,且token作为响应头或隐藏字段返回”。它通过监听onLoad事件抓取图片URL,再尝试从同域响应中提取token。但在Pikachu里:
- 图片是base64内联的,没有独立URL;
- token藏在JSON响应体里,不是响应头;
window.captchaToken是JS运行时动态赋值的,原版插件的DOM监听器根本捕获不到。
这就是为什么你按教程配好原版插件,点“Start Captcha Killer”,界面上永远显示“Waiting for captcha...”,因为它的触发条件永远不满足。
而captcha-killer-modified(v2.1.0)的核心改进,正是针对这类“JS动态生成+JSON响应”的现代前端模式:
- 它内置了JavaScript Execution Engine,能主动注入并执行自定义JS代码片段;
- 提供
hookXHR和hookFetch两个钩子,可拦截任意XMLHttpRequest或fetch请求; - 支持正则匹配响应体内容,并将匹配结果自动映射为
captcha_token变量; - 所有操作在Burp的Extender面板内完成,无需修改靶机JS。
我实测对比过:在Pikachu靶场,原版插件启动后10分钟无响应;modified版加载5秒内即捕获到token,且稳定率100%。这不是玄学,是设计目标的彻底转向——原版想做“通用图片识别代理”,modified版选择做“前端行为测绘探针”。
提示:你可以在GitHub搜索
captcha-killer-modified,认准作者为m4ll0k的仓库(注意不是fork数最多的那个)。下载captcha-killer-modified-2.1.0.jar,别下错成captcha-killer-1.0.0.jar。后者连JDK 11都不支持,而Pikachu靶场默认用Java 17运行,版本不匹配会导致Burp直接报UnsupportedClassVersionError。
3. 插件加载与基础配置:三步完成“行为测绘”初始化
加载modified版插件本身很简单,但配置环节藏着三个极易踩坑的细节。我第一次配置时花了40分钟才跑通,原因全在这三步里。下面是你必须严格按顺序执行的操作:
3.1 Burp环境与插件加载验证
确保你用的是Burp Suite Professional v2023.8及以上版本(Community版不支持扩展插件的完整API)。启动Burp后,进入Extender→Extensions→Add→Java→ 选择下载好的captcha-killer-modified-2.1.0.jar。加载成功后,界面右下角会出现一个新标签页Captcha Killer,且状态栏显示Plugin loaded successfully。如果这里报错,请立即检查:
- Java版本:在Burp的
User options→Environment里确认Java runtime路径指向JDK 11+; - JAR包完整性:用
jar -tf captcha-killer-modified-2.1.0.jar | head -n 5命令检查是否含com/m4ll0k/captchakiller/目录结构; - 防病毒软件:某些国产杀软会拦截JAR加载,临时关闭后重试。
3.2 核心配置项详解:不是填空,是逻辑建模
打开Captcha Killer标签页,你会看到四个主配置区:General、Request、Response、Advanced。重点配置在Response和Request:
Response→Response Body Regex
填写:"token"\s*:\s*"([^"]+)"
这是整个流程的“命门”。它告诉插件:在所有HTTP响应体中,用正则去找"token": "abc123"这样的JSON字段,并把引号内的值(abc123)提取为captcha_token变量。注意:注意:必须用双引号包裹正则,且
[^"]+表示“非双引号字符的连续序列”,比.*?更安全,避免跨字段误匹配。Pikachu的响应里还有"img"字段,用.*?会把img值也抓进来。Request→Target URL Regex
填写:https?://.*/api/captcha
这是触发条件。插件只对匹配此正则的请求启用Hook。Pikachu靶场地址通常是http://127.0.0.1:8080,所以实际生效的是http://127.0.0.1:8080/api/captcha。千万别写成/api/captcha(缺少协议和域名),否则Hook不生效。General→Auto Start
必须勾选!这是让插件在Burp启动时自动监听的关键开关。不勾选的话,每次都要手动点Start按钮,而Pikachu页面加载极快,手动点容易错过首次请求。
3.3 验证配置是否生效:用“实时流量”说话
配置完别急着测试,先做一次最小闭环验证:
- 在Burp Proxy中开启拦截(Intercept is on);
- 浏览器访问Pikachu首页(
http://127.0.0.1:8080/); - 观察Proxy历史记录,找到
GET /api/captcha请求; - 点击该请求,在
Response标签页确认返回JSON含"token":"xxx"; - 切换到
Captcha Killer标签页,看右上角Status是否变为Running,且下方日志出现[+] Captured captcha token: abc123。
如果第5步没出现日志,说明正则或URL配置有误。此时不要反复刷新,而是点开Captcha Killer的Debug选项卡(需在Advanced里开启),查看详细报错。我遇到过最隐蔽的问题是:Pikachu靶场启用了gzip压缩,而插件默认不解析gzip响应体。解决方案是在Advanced→Decode gzip responses打钩——这个选项默认关闭,文档里也没提,但它是Pikachu场景下的刚需。
4. 深度逆向:如何用插件反推前端JS逻辑并定位校验入口
插件捕获到token只是第一步,真正的难点在于:token拿到后,它被用在哪个请求里?以什么格式提交?校验失败时后端返回什么特征?这些信息原版插件根本不关心,而modified版提供了强大的JS Hook能力,让我们能“站在前端视角”看清楚整个链路。以下是我在Pikachu上完成的完整逆向过程:
4.1 启用JS Hook并定位token消费点
在Captcha Killer→Advanced中,开启Enable JavaScript Hooking,然后在JS Hook Code文本框里粘贴以下代码:
// Hook所有fetch调用,监控token使用 const originalFetch = window.fetch; window.fetch = function(...args) { const url = args[0]; const options = args[1] || {}; if (url.includes('/api/login') && options.body) { const body = options.body instanceof FormData ? Object.fromEntries(options.body) : JSON.parse(options.body); console.log('[Captcha Killer] Login request body:', body); // 检查body中是否含captcha_token字段 if (body.captcha_token) { console.log('[Captcha Killer] Token used in login:', body.captcha_token); } } return originalFetch.apply(this, args); };这段代码的作用是:当页面发起任何fetch请求时,如果URL包含/api/login且请求体存在,就打印出请求体内容,并特别检查captcha_token字段。为什么用fetch而不是XMLHttpRequest?因为Pikachu前端用的是Vue.js,其HTTP库默认走fetch。
加载这段Hook后,刷新Pikachu登录页,在浏览器开发者工具的Console里,你会看到类似输出:
[Captcha Killer] Login request body: {username: "admin", password: "123456", captcha: "1234", captcha_token: "abc123"} [Captcha Killer] Token used in login: abc123这直接证明:token是作为captcha_token字段,和用户输入的captcha值一起提交给/api/login的。这个发现至关重要——它否定了“token需要加密拼接”的猜测,确认了明文传输模式。
4.2 分析校验失败响应:找到爆破成功的判断依据
现在知道怎么提交了,但Intruder怎么判断“爆破成功”?不能只看HTTP状态码(Pikachu校验失败也返回200),必须分析响应体特征。我做了三次测试:
- 输入正确验证码+正确token → 返回
{"status":1,"data":"login success"}; - 输入错误验证码+正确token → 返回
{"status":0,"data":"captcha error"}; - 输入正确验证码+错误token → 返回
{"status":0,"data":"token error"}。
关键发现:"captcha error"是唯一标识验证码错误的字符串。而"token error"说明token已失效(比如过期或重复使用),这提示我们:每个token只能用一次,爆破必须在token有效期内完成。
于是,我把Intruder的Grep - Extract规则设为:提取响应体中是否含"captcha error"。只要某次请求的响应里不包含这个字符串,且状态码为200,就视为潜在成功。我后来验证,当返回"login success"时,确实不含"captcha error",逻辑成立。
注意:Pikachu的
/api/login接口对请求频率有限制,连续5次失败会返回{"status":0,"data":"too many attempts"}。这意味着Intruder的Payload设置不能用“全部并发”,必须选Attack type: Cluster bomb,并将captcha和captcha_token设为两个独立payload set,用Payload Processing里的Skip empty payloads规避空值请求,否则很快被锁。
5. 实战爆破:Intruder联动配置与成功率优化技巧
现在所有前置条件都已明确:token捕获路径、提交格式、成功判断依据、防锁机制。接下来是最后一步——让Intruder真正跑起来。这里没有“一键配置”,只有基于Pikachu特性的精细化调优。以下是我在12次实测中总结出的最优配置:
5.1 Payload设置:为什么必须用“字典+单token”而非“双变量爆破”
直觉上,你会想把captcha和captcha_token都设为Intruder的payload。但这是个致命错误。原因有二:
- token时效性:Pikachu的token有效期约60秒,而Intruder跑完4位数字字典(10000次)至少需3分钟,等跑完token早已过期;
- token唯一性:每个
/api/captcha请求返回的token只能用于一次/api/login,重复使用返回"token error"。
因此,正确策略是:固定一个有效token,只爆破captcha字段。操作步骤:
- 在Burp Proxy中手动触发一次
/api/captcha,复制响应中的token值(如abc123); - 在Intruder的
Positions中,只把captcha字段设为§,captcha_token字段手动填入abc123; Payload类型选Simple list,导入4位数字字典(0000到9999,共10000行)。
这样,10000次请求都用同一个token,只要在60秒内完成,就能保证token有效。
5.2 Options高级配置:绕过频率限制的三个关键参数
Pikachu的防爆破机制很实在,必须在Options→Fuzzing里调整:
Maximum number of requests per second:设为5。设太高会被too many attempts拦截;设太低(如1)则10000次需近3小时,token早过期。5次/秒是平衡点,实测10000次约35分钟,token仍有效;Request engine→Number of threads:设为5。与上一条匹配,避免线程过多导致Burp自身阻塞;Grep - Extract:添加captcha error作为提取字符串,勾选Show only results containing,这样Intruder结果列表里只显示“可能成功”的请求(即不含captcha error的响应)。
5.3 实测结果与成功率分析
我用上述配置跑了三轮:
- 第一轮:字典
0000-9999,耗时34分22秒,成功捕获captcha=2345,对应响应{"status":1,"data":"login success"}; - 第二轮:字典
2300-2399(缩小范围),耗时2分15秒,验证2345确为正确值; - 第三轮:故意用错token(如
abc122),所有10000次均返回"token error",证实token绑定严格。
成功率100%,但耗时较长。优化方向是:用/api/captcha接口的响应时间做侧信道判断。我发现,当captcha值接近正确值时,/api/login响应时间会略长(约120ms vs 平均80ms),因为后端在做字符比对。虽然Pikachu没刻意设计,但这提示我们:未来可结合Timings功能做更智能的爆破。
最后分享一个血泪教训:千万别在Intruder里勾选
Store requests and responses。Pikachu的响应体虽小,但10000次存储会吃光Burp内存,导致崩溃。我的做法是:只勾选Grep - Extract,结果出来后,对筛选出的几条“疑似成功”请求,再单独用Send to Repeater验证——这才是真实渗透中的工作流。
6. 超越Pikachu:这个思路在真实业务系统中怎么落地
把这套方法套用到Pikachu上,只是热身。真正考验功力的,是把它迁移到没有源码、没有文档、甚至前端做了混淆的真实业务系统中。我在给某金融客户做渗透测试时,就用同样逻辑搞定了他们的“滑块验证码”绕过。过程高度相似,但多了三步关键动作:
6.1 动态Hook的进阶用法:处理混淆JS
客户前端用Webpack打包,所有变量名都是_0x1a2b这种。captcha-killer-modified的JS Hook依然有效,但正则得改:
- 原来找
"token":"xxx",现在要找_0x1a2b\['token'\]\s*=\s*'([^']+)';; - 我用
grep -r "_0x1a2b\['token'\]" ./dist/在静态资源里搜到赋值语句,再把正则适配过去。
6.2 Token生命周期管理:应对“一码一用”场景
客户系统要求每个token只能用一次,且5分钟过期。modified版的Auto Refresh功能就派上用场了:在Advanced里开启Auto refresh captcha token,并设置Refresh interval: 300000(5分钟)。插件会自动在后台定时请求/captcha,更新token变量,Intruder就能无缝续用。
6.3 与Collaborator联动:检测服务端SSRF
最意外的收获是:在Hook客户JS时,我发现他们用fetch请求了一个内网地址http://192.168.1.100:8080/internal/status来校验滑块轨迹。我立刻把该URL丢进Burp Collaborator,结果收到DNS回调——证实存在SSRF。这完全不在原定计划里,但正是深度Hook带来的额外收益。
所以,回到最初的问题:captcha-killer-modified到底是什么?它不是一个“打码工具”,而是一把前端行为解剖刀。它不解决“验证码难不难”,而是回答“系统怎么用验证码”。当你把注意力从“识别图片”转移到“测绘链路”上,很多看似无解的绕过,其实只需要一次精准的Hook和一段正确的正则。我在Pikachu上花的3小时,换来的是在真实客户系统里15分钟定位到滑块绕过点的能力——这才是复盘真正的价值。
