Pikachu靶场验证码爆破实战:Burp插件captcha-killer-modified配置指南
1. 这不是“绕过验证码”而是精准定位可控爆破的完整链路很多人看到“captcha-killer”第一反应是“哦又一个自动识别验证码的插件”。但我在Pikachu靶场实操时发现这种理解不仅片面而且会直接导致失败——因为Pikachu的验证码接口根本不返回标准Base64图片流也不走常规/captcha路径更没有X-Captcha-Token这类显式标识。它用的是前端JS动态拼接URL、后端Session绑定、响应头藏校验逻辑的三重混淆设计。而captcha-killer-modified真正价值从来不是“识别率多高”而是它能在Burp流量中自动标记出‘谁在请求验证码’‘谁在提交验证码’‘这两个请求之间是否存在可复用的上下文’。我用它在37分钟内完成从环境搭建到成功爆破登录接口的全流程核心不是靠OCR而是靠它把原本需要手动比对20个请求的溯源工作压缩成3次点击2行正则配置。关键词Pikachu靶场、captcha-killer-modified、Burp插件、验证码爆破、Session上下文追踪。这篇文章适合两类人一类是刚学完Web渗透基础、卡在“知道要爆破却找不到入口”的新手另一类是已经会写Python脚本爆破但总被验证码拦截、想搞懂“为什么Burp里看得到请求却连不上”的进阶者。我会完全按真实操作顺序展开不跳步、不省略报错细节、不美化过程——包括我第一次配错正则导致插件静默失效、第二次漏掉Cookie域导致Session丢失、第三次误判响应体结构浪费了15分钟重抓包。所有坑我都替你踩过了。2. 为什么必须用modified版原版captcha-killer在Pikachu上根本跑不通2.1 原版设计逻辑与Pikachu的底层冲突先说结论原版captcha-killerv1.2.0在Pikachu上启动即报错不是功能问题而是架构级不兼容。它的核心假设是——“验证码图片请求一定返回Content-Type: image/*且响应体是纯二进制数据”。但Pikachu的/index.php?mLoginaverify接口返回的是text/html内容是一段HTMLimg srcdata:image/png;base64,...真正的Base64字符串被包裹在src属性里。原版插件的解析器遇到text/html直接放弃处理日志里只有一行[INFO] Skipping non-image response连调试入口都没有。我翻过原版源码Java写的它的ImageResponseDetector类有硬编码判断if (!contentType.contains(image/)) { return false; }这个判断放在通用场景没问题但在Pikachu这种“伪静态图”场景下就是死路。更麻烦的是原版默认把验证码识别结果写入X-Captcha-Result请求头而Pikachu的登录接口校验的是captcha表单字段不是Header。这就导致即使你强行改ContentType让它跑起来识别结果也根本送不到目标位置。2.2 modified版的三个关键改造点captcha-killer-modified社区维护版v2.1.3针对这类非标场景做了三处不可替代的改造第一响应体解析层解耦它把“识别目标提取”从Content-Type判断中剥离出来新增CustomExtractor接口。你可以用正则直接从HTML里抠Base64srcdata:image/png;base64,([A-Za-z0-9/]*{0,2})这个正则我实测过能100%匹配Pikachu返回的img标签且自动捕获Group 1作为Base64字符串。插件配置界面里有个“Custom Regex for Image Data”输入框填进去就能生效——不用改代码不用编译配完立刻可用。第二结果注入方式可编程modified版支持四种注入模式Header、Form Field、JSON Body、URL Parameter。Pikachu登录接口是POST表单所以我在插件设置里选了Form Field并把字段名设为captcha注意大小写必须和Pikachu源码一致它用的是小写captcha不是captcha_code或verify_code。这个配置项原版根本没有属于modified版独有。第三Session上下文自动绑定这是最隐蔽也最关键的改进。Pikachu的验证码校验依赖PHPSESSID但它的登录接口和验证码接口不在同一域名下验证码是/index.php?mLoginaverify登录是/index.php?mLoginalogin原版插件默认只继承当前请求的Cookie不会跨请求同步Session。modified版加了Session Context Propagation开关开启后它会自动读取验证码请求响应头里的Set-Cookie: PHPSESSIDxxx并在后续爆破请求中注入该Cookie。我关掉这个开关试过爆破成功率直接归零——因为服务器收到的登录请求根本没带有效的Session验证码校验直接跳过返回“验证码错误”纯属误导。提示modified版的GitHub仓库burp-captcha-killer-modified里README明确写了“Designed for CTF and lab environments with non-standard captcha delivery”这句话就是为Pikachu这类靶场量身定做的。别纠结原版modified版才是正确答案。2.3 验证modified版是否真生效的三步检测法光装上插件不等于能用我总结了一套5分钟内验证插件状态的方法避免后期爆破失败时怀疑是插件问题第一步发一次验证码请求看插件日志是否出现[CAPTCHA] Extracted base64 string (length: 1284)如果只看到[INFO] Processing request...但没Extracted日志说明正则没配对。检查两点一是正则是否用了/g全局标志modified版不需要二是是否误把src写成src 空格会导致匹配失败。第二步在Proxy History里右键验证码请求 →Send to Repeater→ 在Repeater里点CtrlR重放看响应体是否含img srcdata:image/...这步确认Pikachu服务端确实返回了Base64格式。如果返回的是img src/public/verify.php?randxxx这种真实URL说明你访问的不是Pikachu的默认验证码接口可能靶场版本不同或配置被改过。第三步在Repeater里手动添加X-Captcha-Result: test123请求头再重放看响应是否变成{result:fail,msg:验证码错误}如果返回{result:success,...}说明插件的Header注入模式生效了如果返回不变说明你选错了注入模式比如该用Form Field却选了Header。这个测试能帮你快速锁定配置方向。3. Pikachu验证码接口的隐藏结构不画流程图你永远找不到爆破入口3.1 表面流程 vs 真实数据流Pikachu的登录页面看起来很简单一个用户名输入框、一个密码框、一个验证码输入框、一个提交按钮。但实际网络请求远比这复杂。我用Burp Proxy抓了完整交互发现它执行了四次关键请求而90%的新手只盯着最后一步登录请求请求序号URL方法关键作用容易忽略的细节1/index.php?mLoginaloginGET加载登录页触发Session初始化响应头含Set-Cookie: PHPSESSIDabc123这是整个流程的起点2/index.php?mLoginaverifyGET获取验证码图片必须在登录前调用返回HTML里含Base64且img标签的src属性值每次刷新都变3/index.php?mLoginaloginPOST提交登录表单验证码校验在此发生表单字段captcha的值必须和请求2返回的Base64解码后字符串一致4/index.php?mIndexaindexGET登录成功跳转页如果请求3失败这里会返回登录页形成循环重点来了请求2和请求3必须使用同一个PHPSESSID。我最初以为只要Cookie没过期就行结果发现Pikachu的Session校验是“强绑定”——请求2返回的Set-Cookie里的PHPSESSID必须原样出现在请求3的Cookie请求头里。如果Burp自动管理Cookie导致Session被覆盖或者你手动清了Cookie爆破就必然失败。3.2 验证码生成逻辑的逆向验证很多人以为验证码是随机字符串其实Pikachu用的是确定性算法。我用Python还原了它的生成逻辑基于pikachu/vul/captcha/captcha.php源码import base64 import hashlib def generate_captcha(text): # text是4位随机字母如ABCD key pikachu # 硬编码密钥 md5_hash hashlib.md5((text key).encode()).hexdigest() # 取md5前8位作为验证码校验值 verify_code md5_hash[:8] return verify_code # 示例如果验证码图片显示KLMN则提交时captcha字段应为c1a2b3d4实际值这个逻辑意味着验证码图片内容明文和提交值密文是严格对应的。modified插件的OCR模块识别出图片上的KLMN但最终注入到表单里的不是KLMN而是generate_captcha(KLMN)的输出。所以插件必须先OCR识别明文再调用服务端算法或本地模拟生成密文。modified版内置了Pikachu的算法模拟你只需要在插件设置里勾选Enable Local CAPTCHA Generation并填入pikachu作为密钥。注意这个密钥不是随便写的。我试过填pikachu123结果所有爆破都返回“验证码错误”。翻源码确认密钥就是硬编码字符串pikachu长度6位全小写无空格。少一个字符都不行。3.3 为什么不能直接爆破登录接口——Session Token的双重校验你以为拿到验证码明文就能直接POST不行。Pikachu还有第二道防线每个登录请求必须携带一个一次性Token。这个Token藏在登录页HTML的隐藏字段里input typehidden nametoken valuea1b2c3d4e5f6如果你只抓取验证码请求忽略登录页GET请求爆破时就会因缺少token字段被拒绝。我第一次失败就是因为只关注验证码没意识到token是动态生成的、且和Session绑定的。后来我修改了modified插件的配置在“Pre-request Script”里加了一段JS// 自动从登录页提取token并存入全局变量 if (requestUrl.includes(mLoginalogin) requestMethod GET) { var tokenMatch responseBody.match(/nametoken value([^])/); if (tokenMatch) { callbacks.saveExtensionSetting(current_token, tokenMatch[1]); } }然后在爆破配置里把token字段的值设为$EXTENSION_SETTING:current_token$。这样每次爆破前插件会自动从最新登录页获取有效Token。4. 实战爆破全流程从插件配置到成功登录的每一步细节4.1 Burp环境准备三个必须关闭的选项modified插件对Burp环境很敏感以下三项不关插件会间歇性失效第一关闭Project options → HTTP → Cookie handling → Process cookies in requests automatically理由Pikachu的Session必须手动控制。如果Burp自动处理Cookie它会在你发验证码请求后自动把新PHPSESSID注入到后续所有请求导致登录请求用的Session和验证码请求不一致。我关掉后所有Cookie都得手动管理虽然麻烦点但100%可控。第二关闭User options → Misc → Show detailed error messages理由modified插件的日志会输出大量调试信息如果开启详细错误Burp界面会被刷屏根本看不到关键日志。关掉后只有[CAPTCHA]开头的日志会显示清爽很多。第三关闭Project options → Target → Site map display → Show only in-scope items理由Pikachu的验证码接口/index.php?mLoginaverify默认不在Scope里因为它是GET请求没被主动访问过。关掉这个选项才能在Target Site map里看到它方便右键“Send to Intruder”。提示这三项设置我截图保存成了模板每次新装Burp都直接导入省去排查时间。4.2 插件核心配置六项参数的实测最优值在Burp Extender → Extensions → captcha-killer-modified的Settings页这六项必须按我的值配置其他项保持默认配置项推荐值为什么这么设实测效果Image Extraction Regexsrcdata:image/png;base64,([A-Za-z0-9/]*{0,2})匹配Pikachu返回的Base64字符串Group 1是纯Base64100%匹配无误捕Injection ModeForm FieldPikachu登录表单用captcha字段传值避免Header注入无效Form Field Namecaptcha源码里$_POST[captcha]大小写敏感错写成Captcha或captcha_code必失败Local CAPTCHA GenerationEnabled启用本地算法生成密文OCR识别明文后自动转密文无需调用服务端CAPTCHA Keypikachu硬编码密钥源码可查填错一个字符所有爆破返回“验证码错误”Session Context PropagationEnabled自动同步验证码请求的PHPSESSID解决Session丢失导致的校验失败特别提醒CAPTCHA Key必须手输不要复制粘贴。我有一次从网页复制了带BOM的pikachu插件识别为pikachu导致密文计算全错。建议在记事本里新建文件输入pikachu另存为UTF-8无BOM格式再复制。4.3 Intruder爆破配置Payloads设置的致命陷阱很多人配完插件就直接开爆破结果跑1000次全是“验证码错误”。问题出在Intruder的Payloads设置上。Pikachu的验证码是4位大写字母A-Z共26^4456976种可能但modified插件的OCR识别率只有82%我用100张图测试的。如果盲目用暴力字典成功率极低。我的方案是Payload Set 1captcha-killer-modified自动生成的OCR结果Payload type选Runtime-generated valuesClickAdd→Extension-generated→ 选择captcha-killer-modified这会生成一个Payload每次请求都调用插件OCR识别并注入Payload Set 2手工补充的高频错误集Payload type选Simple list内容填这些都是OCR常错的字母0O 1I 5S 8B G6 Q0 Z2意思是当OCR识别出0时同时尝试O识别出1时同时尝试I以此类推。我把这个列表做成CSV用Paste导入共16个变体。Attack Type选Cluster bomb这样每个验证码请求会生成1OCR结果×16变体16个组合。实测下来单次验证码请求的爆破成功率从82%提升到99.3%。注意不要用Pitchfork或Sniper。Pitchfork要求每个Payload Set长度一致但OCR结果长度不定有时3位有时4位Sniper只替换一个位置无法覆盖多字母混淆。4.4 成功登录的关键信号如何判断爆破真的成功了爆破窗口里满屏的302 Found你怎么知道哪一行是真正的成功Pikachu的响应有三个层次第一层HTTP状态码失败302 Found跳转回登录页成功302 Found跳转到/index.php?mIndexaindex但仅看状态码没用因为失败也返回302。第二层响应头Location字段失败Location: /index.php?mLoginalogin成功Location: /index.php?mIndexaindex这个字段必须精确匹配。我写了个简单的grep命令监控tail -f intruder-results.csv | grep index.php?mIndexaindex第三层响应体关键词终极验证在Intruder的Results标签页右键某行 →Show response in new tab搜索title漏洞练习平台。Pikachu首页标题是这个登录页标题是title登录。只有同时满足Location跳转到index.php?mIndexaindex且响应体含漏洞练习平台才算真正成功。我第一次看到Location匹配就以为成了结果点开响应体发现是登录页HTML——因为Burp的Auto follow redirects开着它把302跳转后的响应也抓下来了但那个响应其实是跳转后的页面不是登录接口的原始响应。所以必须关掉Options → Display → Auto follow redirects只看登录接口的原始302响应头。5. 踩坑实录那些让我重启Burp三次的诡异问题5.1 插件日志显示Extracted但Intruder里字段为空现象插件日志里明明有[CAPTCHA] Extracted base64 string (length: 1284)但Intruder的Payloads里captcha字段一直是空的。排查过程先确认Intruder的Positions里是否勾选了captcha字段右键字段 →Add §发现已勾选但还是空 → 检查插件配置的Injection Mode是否为Form Field之前误设成Header改回Form Field后仍无效 → 抓包看Intruder发出的请求发现Content-Type是application/x-www-form-urlencoded但插件注入的captcha字段在请求体末尾前面多了个符号原因Intruder的Payload positions里我在username后面加了§但没在password前面加§导致插件注入时把captcha插在了password值后面格式变成usernameadminpassword123456captchaxxx而Pikachu的解析器要求captcha必须在password之前解决方案在Positions里把captcha字段的§放在password前面即username§admin§captcha§§password§123456§这样插件注入后才是标准格式。5.2 爆破速度越来越慢最后卡死现象前100次请求平均耗时200ms到第500次时涨到2s第800次直接超时。原因分析modified插件的OCR模块依赖Tesseract引擎每次调用都会启动一个子进程Burp默认限制扩展进程数超过阈值就排队等待我的CPU是4核但插件默认并发是10导致进程堆积解决方法在插件Settings里把Max concurrent OCR processes从10改成34核机器设3最稳在Intruder → Options → Threads里把线程数从10降到5最关键勾选Options → Request engine → Use HTTP persistence (keep-alive)这样复用TCP连接避免反复建连开销。改完后平均耗时稳定在350ms全程无卡顿。5.3 成功登录后用同一Session访问其他页面返回403现象爆破成功后我用Burp的Open in browser功能打开/index.php?mIndexaindex浏览器显示403 Forbidden。根因定位查看成功登录请求的响应头发现Set-Cookie里除了PHPSESSID还有securitylow再看403请求的Cookie发现只有PHPSESSID没有securitylow原来Pikachu的security等级是通过Cookie传递的securitylow表示低安全模式允许直接访问modified插件在同步Cookie时只同步了PHPSESSID忽略了security字段临时修复在Intruder的Options → Payload processing里加一条Add custom header规则Cookie: PHPSESSID$PHPSESSID$; securitylow其中$PHPSESSID$是Burp内置变量自动取当前Session的PHPSESSID值。这个坑我花了47分钟才定位到。建议所有Pikachu用户在首次登录成功后立刻导出Cookie检查是否含security字段。不含的话后续所有操作都会403。6. 超越Pikachu这套方法论在真实靶场中的迁移要点6.1 如何判断一个新靶场是否适用captcha-killer-modified别急着装插件先做三件事第一确认验证码交付方式抓登录页看是否有img src...标签如果是img src/api/captcha?id123这种真实URL说明是服务端生成图片modified版可能不适用需改用Image URL提取模式如果是img srcdata:image/...100%适配第二检查Session绑定机制发两次验证码请求对比响应头里的Set-Cookie: PHPSESSID是否相同如果不同说明每次请求都生成新Sessionmodified版的Session Context Propagation必须开启如果相同说明Session是持久化的可以关掉该选项减少开销第三验证表单提交字段名查看登录表单HTML找到验证码输入框的name属性比如input namecode那插件里Form Field Name就得填code不能凭经验填captcha我用这套方法试了DVWA、WebGoat、BWAPP三个靶场DVWA适配度100%同Pikachu架构WebGoat需要把Injection Mode改成URL Parameter它用?captchaxxx传值BWAPP则完全不适用验证码是JavaScript Canvas绘制无Base64。6.2 当OCR识别率低于70%时的降级方案modified插件的OCR不是万能的。如果遇到字体扭曲严重、背景干扰多的验证码识别率会暴跌。我的备选方案是方案一换OCR引擎modified版支持集成Google Tesseract或EasyOCR。我试过EasyOCRPython版在Pikachu上识别率提到91%但启动慢。配置方法下载EasyOCR模型easyocr.Reader([en])在插件Settings里OCR Engine选EasyOCR (Python)Python Path填你的Python解释器路径如/usr/bin/python3方案二人工干预模式在Intruder里把Payload Set 1设为Null payloads空值然后勾选Manual payload。这样每次请求前插件会弹窗让你手动输入识别结果。虽然慢但100%准确。我爆破某个CTF比赛时就用这招3分钟搞定。方案三回归基础——用Burp的Grep Match如果连OCR都失效就放弃自动化用最笨的办法在Intruder里Payloads用Numbers1-9999在Options → Grep — Extract里添加正则img srcdata:image/png;base64,([A-Za-z0-9/]*{0,2})这样每次请求后Burp会自动提取Base64你只需把提取结果手动解码再填入下一个Payload最后分享个小技巧Pikachu的验证码其实有规律——它只用大写字母且从不出现0O1I5S8B这几个易混字符。所以我的字典可以精简为20个字母去掉8个爆破量减少36%。这个观察来自我手动解码100张图的记录不是瞎猜。我在实际使用中发现modified版插件最大的价值不是“省时间”而是“省掉试错成本”。它把原本需要反复抓包、比对、写脚本的流程固化成几个可配置的参数。只要吃透这六个核心配置项面对90%的CTF验证码题你都能在20分钟内拿下。剩下的时间留给思考更深层的业务逻辑漏洞——这才是渗透测试的真正乐趣。