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-killerv1.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内联的没有独立URLtoken藏在JSON响应体里不是响应头window.captchaToken是JS运行时动态赋值的原版插件的DOM监听器根本捕获不到。这就是为什么你按教程配好原版插件点“Start Captcha Killer”界面上永远显示“Waiting for captcha...”因为它的触发条件永远不满足。而captcha-killer-modifiedv2.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 11JAR包完整性用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和RequestResponse→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字段手动填入abc123Payload类型选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秒成功捕获captcha2345对应响应{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: 3000005分钟。插件会自动在后台定时请求/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分钟定位到滑块绕过点的能力——这才是复盘真正的价值。