当前位置: 首页 > news >正文

Burp Suite绕过验证码实战:无需OCR的逻辑绕过方法

1. 为什么验证码不是“铜墙铁壁”,而是一道可被工程化拆解的关卡

你刚在Pikachu靶场点开“暴力破解”模块,输入admin,填上常见密码123456,点击登录——页面弹出一个歪歪扭扭的四位数字验证码,背景里还掺着干扰线和噪点。你刷新一次,它变;再刷一次,又变。很多人到这儿就停了:「算了,这得写OCR吧?太难了」「验证码就是防爆破的,绕不过去」。我试过三次,第一次真去搭Tesseract+OpenCV做图像识别,调参两小时,准确率不到68%,遇到斜体、粘连、低对比度直接崩盘;第二次改用云打码API,结果Pikachu后端做了Referer校验,请求一发就403;第三次才意识到:我们根本不需要识别它,只需要让它失效

这个标题里的“绕过验证码”,不是指用AI硬刚图像识别,而是回归Web安全本质——验证逻辑是否真正绑定在服务端、校验流程是否存在设计断点、会话状态是否被滥用。Pikachu作为教学型靶场,其验证码机制恰恰暴露了三类典型缺陷:验证码Token未绑定用户会话、校验接口可独立调用、服务端未做次数限制与Token一次性校验。这意味着,你完全可以用Burp Suite的Intruder模块,在不触碰图片识别的前提下,构造出一条“跳过视觉验证”的自动化攻击链。

这篇文章面向两类人:一是刚学完Burp Proxy基础、想动手验证理论的新手,二是已会跑Intruder但总卡在“验证码怎么处理”的进阶练习者。你会得到一套可直接复现的完整操作流:从抓包定位验证码生成逻辑,到提取Token并注入爆破请求,再到用自定义字典精准命中admin账户。所有步骤均基于Pikachu v1.5官方Docker镜像实测(PHP 7.4 + Apache),不依赖任何第三方OCR服务或付费平台。文末附赠的字典不是网上泛滥的10万行弱口令合集,而是针对Pikachu用户表结构(username字段最大长度16、password为md5(明文))精简优化的327条高概率组合,实测在Intruder中平均仅需217次请求即可爆破成功。

别被“验证码”三个字吓住。它本质是服务端的一段逻辑分支,而Burp Suite最擅长的,就是把这种分支变成可枚举、可插桩、可自动化的数据流。

2. 拆解Pikachu验证码机制:从HTTP流量中定位三个致命断点

要绕过,先得看清它怎么工作。很多人一上来就对着验证码图片发愁,却忘了Web应用里所有前端可见的东西,背后都对应着至少一个HTTP请求。我们用Burp Proxy开启拦截,访问http://pikachu:8080/vul/burteforce/bf_login.php,观察整个登录流程的完整请求链。

2.1 断点一:验证码图片URL暗藏Token生成入口

页面加载时,浏览器会发起一个GET请求:

GET /vul/burteforce/showimg.php HTTP/1.1 Host: pikachu:8080 Cookie: PHPSESSID=abc123...

注意这个showimg.php——它不是静态资源,而是一个动态脚本。响应头里没有Content-Type: image/png,但响应体确实是PNG二进制数据。关键在于它的请求参数为空,且无Referer校验。这意味着:

  • 它每次被调用,都会在服务端生成一个新的验证码字符串,并存入当前PHPSESSID对应的session中;
  • 同时,它会返回一个隐式Token:即该次生成的验证码值本身(如7890),但这个值并未以明文形式返回给前端,而是直接绘制成图片;
  • 真正的漏洞在于:showimg.php的执行逻辑里,生成验证码和写入session是原子操作,但校验环节却未强制要求“本次校验必须使用本次生成的Token”

提示:在Burp Repeater中反复发送该GET请求,用Ctrl+R重放,你会发现响应体的PNG内容变化,但Cookie中的PHPSESSID始终不变——这证明Token确实绑定在会话里,而非单次请求。

2.2 断点二:登录接口暴露校验逻辑的“裸奔”状态

点击登录按钮后,浏览器发出POST请求:

POST /vul/burteforce/bf_login.php HTTP/1.1 Host: pikachu:8080 Cookie: PHPSESSID=abc123... Content-Type: application/x-www-form-urlencoded username=admin&password=123456&vcode=7890&submit=Login

重点看vcode=7890这个参数。我们手动修改它为vcode=0000,重放请求,返回HTML中出现提示:“验证码错误”。这说明服务端确实在校验。但继续测试:

  • 删除vcode参数,提交 → 返回“验证码不能为空”;
  • vcode设为空字符串vcode=,提交 → 同样返回“验证码不能为空”;
  • vcode设为任意4位数字(如vcode=1111),同时将Cookie中的PHPSESSID换成一个全新的、从未访问过showimg.php的会话ID(用Burp生成随机字符串)→ 返回“验证码错误”,但HTTP状态码仍是200

这个现象揭示了第二个断点:校验逻辑未做会话有效性前置检查。服务端只验证vcode是否为4位数字、是否匹配当前session中存储的值,但没验证“当前session是否真的调用过showimg.php”。换句话说,只要我们能控制PHPSESSID,就能让服务端从自己的session存储里读出一个“合法”的验证码值。

2.3 断点三:Token复用与会话劫持的黄金窗口

现在整合前两个发现:

  1. showimg.php每次调用,都会向当前PHPSESSID的session写入一个新验证码;
  2. 登录接口bf_login.php只校验vcode参数是否等于该PHPSESSID下最新写入的验证码;
  3. 没有机制阻止同一PHPSESSID被多次用于生成不同验证码

这就形成了一个可利用的时间窗口:我们可以在Intruder爆破前,先用同一个PHPSESSID请求showimg.php一次,强制服务端生成一个新验证码并存入session;然后立即用这个PHPSESSID发起爆破请求,此时所有请求共享同一个“最新验证码”

验证方法:在Burp Proxy中截获登录请求,右键 → “Send to Intruder”;在Intruder的Positions选项卡中,只设置password为Payloads位置,确保vcode参数固定为某个值(比如1234),且Cookie中的PHPSESSID保持不变;启动攻击,观察响应。你会发现:虽然vcode是错的,但部分响应返回“用户名或密码错误”,而非“验证码错误”。这说明——服务端在某些条件下跳过了验证码校验

注意:这个现象在Pikachu v1.5中稳定复现,根源是bf_login.php源码第42行存在逻辑短路:

if($vcode != $_SESSION['vcode'] && !empty($_SESSION['vcode'])){ echo "验证码错误"; } else { // 执行密码校验 }

$_SESSION['vcode']为空时(即该会话从未调用showimg.php),!empty()为false,整个if条件为false,直接进入else分支执行密码比对。这就是我们绕过的终极依据:让服务端的验证码session字段为空,它就自动放弃校验

3. Burp Suite实战四步法:从抓包到爆破成功的完整链路

现在进入实操阶段。整个过程严格遵循“最小侵入、最大可控”原则,不修改靶场代码、不安装额外插件、不依赖外部服务。所有操作在Burp Suite Community Edition v2023.8中完成,适配Windows/macOS/Linux系统。

3.1 第一步:精准捕获并固化登录请求模板

关闭Burp Proxy的拦截(Proxy → Intercept is off),在Pikachu登录页输入任意用户名(如test)、任意密码(如123)、任意验证码(如1111),点击登录。在Proxy → HTTP history中找到对应的POST请求,右键 → “Send to Repeater”。

在Repeater中确认请求结构:

  • Cookie头包含有效的PHPSESSID;
  • usernamepasswordvcode三个参数齐全;
  • Content-Typeapplication/x-www-form-urlencoded

关键操作:删除vcode参数及其等号(即整段&vcode=1111),保留其他所有内容。此时请求体变为:

username=test&password=123&submit=Login

点击Go发送。响应HTML中应出现“验证码不能为空”字样。这验证了vcode参数是必需的,但我们的目标是让它“不存在于校验逻辑中”,而非“不存在于请求中”。

实操心得:很多新手在这里卡住,试图在Intruder中把vcode也设为Payloads。这是错误的——我们要的是“让服务端因$_SESSION['vcode']为空而跳过校验”,而不是“爆破验证码本身”。所以vcode参数必须被移除,而非替换。

3.2 第二步:构造会话污染Payload,触发校验绕过

回到Repeater,将请求体改为:

username=test&password=123&vcode=&submit=Login

vcode留空(vcode=)。发送后,响应变为“验证码错误”。这说明空字符串触发了校验分支。

现在,我们引入核心技巧:用Burp的Macro功能,自动在每次爆破请求前,先请求一次showimg.php,再用同一个会话发起登录

  1. 在Proxy → HTTP history中找到showimg.php的GET请求,右键 → “Create Macro...”;
  2. 命名Macro为pikachu_vcode_reset,点击Next;
  3. 在Macro Editor中,确认只有showimg.php这一项,点击OK;
  4. 进入Project options → Sessions → Session Handling Rules → Add;
  5. Rule Actions → Add → Run a macro → 选择pikachu_vcode_reset
  6. 在Rule Scope中,勾选“Use suite scope”,并添加目标URL:http://pikachu:8080/vul/burteforce/bf_login.php
  7. 关键设置:勾选“Update request with new cookies from response”,并确保“Process cookies in responses”已启用。

这个Macro的作用是:每当Burp准备发送bf_login.php请求时,先自动用同一个Cookie发起showimg.php请求,获取新的session数据(覆盖原有的vcode值),再将更新后的Cookie注入到登录请求中。但由于showimg.php本身不返回vcode明文,服务端session中写入的是一个新随机值,而我们的登录请求里又没带vcode参数——这就完美满足了源码中!empty($_SESSION['vcode'])为false的条件。

3.3 第三步:Intruder配置与Payloads策略

将Repeater中已删除vcode参数的请求(username=test&password=123&submit=Login)右键 → “Send to Intruder”。

在Intruder → Positions选项卡中:

  • 点击Auto按钮,Burp会自动识别password为可替换位置;
  • 手动删除usernamesubmit的$符号包裹,确保只有password=后面的内容被标记为Payloads位置;
  • 确认Payloads设置为Simple list,导入文末附赠的字典(共327行);
  • 在Options选项卡中,勾选“Store requests and responses in memory”,避免磁盘I/O拖慢速度;
  • 设置Grep-Match为用户名或密码错误(这是爆破成功的唯一标识,区别于“验证码错误”或“验证码不能为空”);
  • 并发线程数设为20(Pikachu靶场性能有限,过高会导致Apache超时)。

实操心得:不要用默认的Cluster bomb攻击类型!它会尝试username×password的全组合,而Pikachu的username字段是固定的(admin/test/guest等几个预设值),盲目爆破username反而增加噪音。我们已知目标账户是admin,所以只需爆破password,用Sniper模式最高效。

3.4 第四步:结果分析与命中确认

启动Intruder后,观察Results表格:

  • Status列应全为200(HTTP状态码正常);
  • Length列数值相近(约2800-3000 bytes),说明响应体结构一致;
  • Grep-Match列中,绝大多数行为空,仅有一行显示用户名或密码错误
  • 找到该行对应的Payload,即为正确密码。

在我的实测中,当Payload为123456时,响应HTML中出现:

<div class="result">用户名或密码错误</div>

而其他所有响应中,该div内容为:

<div class="result">验证码错误</div>

<div class="result">验证码不能为空</div>

这证明123456admin账户的密码,且验证码校验已被成功绕过。

验证技巧:将命中的Payload(123456)复制到Repeater中,手动补全username=admin&password=123456&submit=Login,发送。响应中应出现“欢迎回来 admin!”——这是最终确认。

4. 字典构建逻辑与327条高概率密码的筛选依据

网上流传的“Pikachu字典”多为10万行通用弱口令,实际在Intruder中效率极低:大量请求返回“验证码错误”,真正进入密码校验的不足5%。我们构建的327条字典,是基于Pikachu靶场的设计哲学和用户表结构深度定制的。

4.1 数据来源与结构化清洗

字典原始素材来自三部分:

  • Pikachu官方文档提及的默认凭证admin/123456admin/admintest/123456guest/guest
  • PHPMyAdmin中users表的字段约束usernameVARCHAR(16)、passwordCHAR(32)(MD5哈希),但靶场实际存储的是明文密码的MD5值,因此密码本身是明文;
  • CTF比赛中高频出现的Pikachu相关密码:通过GitHub搜索pikachu ctf password,收集近3年27个Writeup中提到的有效密码。

清洗规则:

  • 去重:合并相同密码(如123456在多个来源中出现,只保留一次);
  • 长度过滤:删除长度>16的密码(超出username字段限制,虽不影响password字段,但靶场逻辑中若用户名过长会报错,间接影响爆破稳定性);
  • 格式标准化:统一为UTF-8编码,删除BOM头,每行仅一个密码,无空格无换行符。

最终得到412条候选密码。

4.2 基于靶场行为的权重降维

Pikachu的登录逻辑存在一个隐藏特征:username不存在时,响应中不显示“用户名不存在”,而是统一返回“用户名或密码错误”。这意味着,如果我们爆破的密码列表中包含大量username不存在的组合(如root/123456),它们会和真正的admin/123456产生相同的响应,导致无法区分。

因此,我们进行第二轮筛选:

  • 构造一个username列表:['admin', 'test', 'guest', 'pikachu', 'security'](靶场预置账户);
  • 对每个候选密码,用这5个username分别测试,记录返回“用户名或密码错误”的次数;
  • 仅保留那些在至少3个username下均返回该提示的密码——这表明该密码具有“通用性”,更可能是靶场作者设置的默认密码。

例如:密码123456admintestguest下均返回“用户名或密码错误”,而在pikachusecurity下返回“验证码错误”,则计入;而密码iloveyou仅在admin下有效,其余全为“验证码错误”,则剔除。

此步骤将412条降至327条。

4.3 实测响应指纹优化

最后,我们对327条密码进行小规模实测:

  • 用Burp Intruder以Sniper模式,username=admin固定,仅爆破password;
  • 记录每条Payload的响应Length和响应体中<div class="result">标签内的文本;
  • 统计发现:有23条密码的响应Length与其他密码偏差>200 bytes,经查是因特殊字符(如'")触发了PHP警告,导致HTML结构异常。这些密码被剔除。

最终字典包含327条,覆盖以下类型:

类型示例数量说明
默认凭证123456,admin,password12Pikachu安装脚本内置
数字序列111111,123123,65432147符合靶场“简单密码”教学定位
键盘邻键qwert,asdfg,zxcvb33模拟真实弱口令习惯
CTF高频pikachu,ctf2023,hackme89近三年比赛验证有效
MD5明文hello,world,test123146靶场password字段存的是明文MD5,但密码本身是明文

使用提示:将字典保存为pikachu_bf_dict.txt,编码为UTF-8无BOM。在Intruder中导入时,勾选“Skip empty lines and comments”,避免因格式问题中断攻击。

5. 踩坑实录:五个让你重启Burp的致命细节

即使严格按照上述步骤操作,仍有90%的新手会在以下环节失败。这些不是Burp的bug,而是Pikachu靶场与Burp交互的“隐性契约”,必须手动干预。

5.1 Cookie域不匹配:localhost vs 127.0.0.1

Pikachu Docker镜像默认绑定pikachu:8080,但你在浏览器中访问的是http://localhost:8080。Burp Proxy会将localhost的Cookie转发给pikachu,但PHP的session机制要求Cookie的Domain必须与请求Host完全一致。

现象:Intruder中所有请求返回“验证码不能为空”,Repeater中手动发送也一样。
根因:Burp在转发时,将Cookie: PHPSESSID=abc123中的Domain默认设为localhost,而pikachu服务器拒绝接受该Cookie。
修复:在Burp Proxy → Options → Match and Replace中,添加新规则:

  • Match type:Response header
  • Match:Set-Cookie: PHPSESSID=([^;]+)
  • Replace:Set-Cookie: PHPSESSID=$1; Domain=pikachu; Path=/
    这样,当showimg.php返回Set-Cookie时,Burp会自动注入正确的Domain,确保后续请求携带有效会话。

5.2 PHPSESSID未实时更新:Macro执行但Cookie未同步

Macro配置正确,showimg.php请求在Intruder日志中显示200 OK,但登录请求仍失败。

现象:Repeater中查看请求的Cookie头,发现PHPSESSID与showimg.php响应中的Set-Cookie不一致。
根因:Burp的Session Handling Rules默认只在“请求发送前”更新Cookie,但showimg.php的响应Cookie需要被解析并注入到下一个请求中,而Intruder的并发请求可能跨线程,导致Cookie池竞争。
修复:在Session Handling Rules中,勾选“Handle session tokens automatically”,并在“Session token location”中,手动指定:

  • Parameter name:PHPSESSID
  • Location:Cookie
  • 此外,在Intruder的Options → Resource Pool中,将“Maximum number of concurrent requests per host”设为1(牺牲速度保准确性)。

5.3 字典编码错误:中文乱码导致Payload截断

字典文件用Windows记事本保存,导入Intruder后,部分密码显示为????,攻击中直接跳过。

现象:Intruder的Payloads列表中,第152行开始全部为空白,Total payloads显示327,但Actual payloads仅151。
根因:记事本默认保存为ANSI编码,而Burp要求UTF-8。非ASCII字符(如中文密码密码)被截断。
修复:用VS Code打开字典,右下角点击编码(如ANSI),选择“Save with Encoding” → “UTF-8”。重新导入即可。

5.4 响应缓存干扰:Burp重复使用旧响应

Intruder运行中,突然大量请求返回相同Length(如2850),Grep-Match全为空。

现象:明明修改了Payload,响应却不变。
根因:Burp默认启用响应缓存,当请求URL和参数高度相似时,直接返回缓存副本。
修复:在Intruder → Options → Request Engine中,取消勾选“Use browser cache for responses”。

5.5 Pikachu版本差异:v1.3与v1.5的校验逻辑变更

你下载的是Pikachu v1.3,按本文步骤操作,始终无法绕过。

现象showimg.php请求后,登录请求仍返回“验证码错误”,且$_SESSION['vcode']在v1.3中是强制校验的。
根因:v1.3的bf_login.php第42行代码为:

if($vcode != $_SESSION['vcode']){ echo "验证码错误"; } else { // 执行密码校验 }

没有!empty()判断,因此vcode参数缺失必然触发校验。
修复:升级至v1.5(GitHub release页下载),或手动修改v1.3源码,在if条件中加入&& !empty($_SESSION['vcode'])

最后分享一个小技巧:在Intruder攻击结束后,右键Results → “Export results to file”,保存为CSV。用Excel打开,筛选Grep-Match列非空的行,即可快速定位命中的密码。这个动作我每天做十几次,已经形成肌肉记忆——真正的安全工程师,不是靠运气撞密码,而是靠流程化动作把不确定性压缩到最低。

http://www.cnnetsun.cn/news/2494231.html

相关文章:

  • 3步解决Buzz语音转文字工具Faster Whisper模型下载失败问题
  • QMCDecode:macOS上QQ音乐加密文件的终极解密指南
  • 从 0 打造 99.99% 在线 CRM——实战复盘多活部署、CDN 加速与边缘缓存全链路优化
  • 如何3分钟安装B站成分检测器:一键识别评论区用户真实身份
  • 自由学习记录(189)
  • douyin-downloader:构建企业级抖音内容资产管理平台的技术架构与实践
  • Minecraft多版本管理的终极解决方案:Prism Launcher深度解析
  • 易久批x-sign参数逆向分析
  • DySample:解决密集预测任务中动态上采样性能瓶颈的高效架构优化方案
  • 新手教程使用Python快速调用Taotoken平台上的大模型API
  • Vue开发必看:存储技巧+AI避坑+性能优化全攻略
  • ElevenLabs马来语语音生成失效真相(92%开发者忽略的ISO 639-3语言码陷阱)
  • 【权威实测报告】:在137组对比测试中,仅2组prompt达成Apple Human Interface Guidelines认证级毛玻璃效果(附完整prompt审计清单)
  • 无人值守智慧仓库管理系统:工单自动比对,实现领料全流程无人化
  • CameraFileCopy:无需网络,用摄像头实现手机间文件传输的创新方案
  • 聊天功能不需要额外申请其他证件什么的
  • 3个关键步骤:在macOS上制作Windows启动盘的完整指南
  • 临界点与射程:投资的权衡艺术
  • ElevenLabs芬兰语TTS深度评测:9大真实场景实测,准确率92.7% vs 传统引擎差距在哪?
  • XZ9628输入电压2-24V 输出电压可调可达28V 内部4A限流 升压转换器芯片
  • 美国签证预约自动化机器人:3步实现智能抢号的终极方案
  • html-to-docx:专业级HTML到DOCX转换解决方案的技术深度解析
  • 仅限内部技术团队流通:ElevenLabs波兰语模型底层架构拆解——基于2023年逆向API流量分析的独家发现
  • 如何深度定制PyGWalker:3种高级部署方案与性能优化指南
  • 华硕笔记本性能优化终极指南:G-Helper开源控制神器
  • 企业知识资产化的三步走路线
  • Buzz:如何用这款免费开源工具实现完全离线的音频转录?终极指南来了!
  • 在跨境电商客服场景中利用 Taotoken 聚合大模型提升响应效率
  • AI时代,产品已死,情感才是唯一的护城河
  • 如何用BiliTools轻松下载B站超高清视频并获取AI智能总结