摘要:这是 bWAPP 系列第三十二篇,聚焦于 Broken Auth. – CAPTCHA Bypassing(验证码绕过)。和之前所有的注入类漏洞不同,这一关是认证绕过漏洞。登录页面加了验证码,本意是防止暴力破解,但因为验证码验证逻辑有缺陷,导致攻击者可以多次尝试密码而不用重新输入验证码。文章会分析三种安全级别的源码差异,演示如何通过 Burp Suite 抓包爆破密码,同时解释验证码应该怎么设计才能有效防御。附真实案例。
一、找目标
| 攻击面 | 登录表单 |
| 防护机制 | CAPTCHA(验证码) |
| 漏洞类型 | 认证绕过——验证码可重复使用 |
| 最终目标 | 绕过验证码限制,暴力破解密码登录成功 |
二、前言:这不是注入,这是认证绕过
如果你一路跟到这个系列,前面三十多篇全是各种注入——SQL 注入、XPath 注入、命令注入……都是通过“注入代码”来攻击。
这一关完全不一样了——没有注入点。
这是一个登录页面,有用户名、密码、验证码三个输入框。正常用户输入正确的账号密码和验证码就能登录。
验证码的作用:防止攻击者用脚本暴力破解密码——因为每次登录都需要输入验证码,而验证码每次不同,自动化脚本很难处理。
但问题来了:如果验证码验证通过后没有被销毁,那攻击者就可以用同一个验证码尝试无数次密码。
这就是验证码绕过。
三、关卡介绍
3.1 页面功能
打开这一关,你会看到一个登录页面:
-
标题:Broken Auth. – CAPTCHA Bypassing
-
三个输入框:Login、Password、Re-enter CAPTCHA
-
一个 “Login” 按钮
-
页面提示默认账号密码是 bee/bug

3.2 正常登录流程
-
输入正确的用户名和密码(bee / bug)
-
输入验证码(图片里显示的内容)
-
点 Login
-
登录成功,显示 “Successful login!”
验证码的作用:每次刷新页面,验证码图片会变。正常用户每次登录都要重新输入验证码。
四、核心知识:CAPTCHA 是什么?
4.1 CAPTCHA 的定义
CAPTCHA(Completely Automated Public Turing test to tell Computers and Humans Apart)——全自动区分计算机和人类的图灵测试。
说白了就是:用来判断你是人还是机器的一段测试。
最常见的形式就是歪歪扭扭的字母数字图片,人眼能认出来,机器很难识别。
4.2 CAPTCHA 的作用
-
防止暴力破解:攻击者用脚本反复尝试密码,验证码让每次尝试都需要人工识别
-
防止刷票:投票系统里防止机器人批量投票
-
防止垃圾评论:评论区防止机器人发广告
4.3 验证码的正确实现方式
一个安全的验证码系统应该做到:
-
每个验证码只能使用一次——验证通过后立即销毁
-
验证码有有效期——过期后自动失效
-
验证码和 Session 绑定——无法被其他用户复用
-
验证码图片足够复杂——无法被 OCR 轻易识别
这一关的问题在哪?
验证码验证通过后,没有被销毁。也就是说,你可以用一个正确的验证码,然后尝试无数个密码,直到找到正确的那个。
五、源码分析
5.1 核心逻辑
if(isset($_POST["form"]))
{
if($_COOKIE["security_level"] == "1" or $_COOKIE["security_level"] == "2")
{
// Medium / High 级别
if(isset($_SESSION["captcha"]) && ($_POST["captcha_user"] == $_SESSION["captcha"]))
{
if($_POST["login"] == $login && $_POST["password"] == $password)
{
$message = "<font color=\\"green\\">Successful login!</font>";
}
else
{
$message = "<font color=\\"red\\">Invalid credentials!</font>";
}
}
else
{
$message = "<font color=\\"red\\">Incorrect CAPTCHA!</font>";
}
}
else
{
// Low 级别
if($_POST["captcha_user"] == $_SESSION["captcha"])
{
if($_POST["login"] == $login && $_POST["password"] == $password)
{
$message = "<font color=\\"green\\">Successful login!</font>";
}
else
{
$message = "<font color=\\"red\\">Invalid credentials!</font>";
}
}
else
{
$message = "<font color=\\"red\\">Incorrect CAPTCHA!</font>";
}
}
}
代码结构:
$_SESSION["captcha"] 存储了当前验证码的字符串。用户提交的 captcha_user 和它比较。
关键缺陷:验证码验证通过后,$_SESSION["captcha"] 没有被清空或重置。所以一旦你输入了一次正确的验证码,就可以一直用同一个验证码值提交请求,直到登录成功。
5.2 三种安全级别的差异
| Low | 只校验验证码和密码 | 不是 | 可绕过 |
| Medium | 先校验验证码再校验密码 | 不是 | 可绕过 |
| High | 先校验验证码再校验密码 | 不是 | 可绕过 |
高和中的区别其实不大:Low 和 Medium/High 的逻辑几乎一样,只是 Medium/High 多了一个 isset($_SESSION["captcha"]) 检查。
真正的核心问题:不管哪个级别,验证码都没有被销毁。
六、Low 安全级别
6.1 正常登录
输入正确的账号密码和验证码:登录成功,显示 “Successful login!”。

6.2 错误密码 + 正确验证码
输入正确验证码,但密码错误:页面显示 “Invalid credentials!”,登录失败。

6.3 暴力破解流程
-
打开页面,刷新验证码图片,记下验证码字符串(比如 ABC123)
-
用 Burp Suite 拦截登录请求
-
在 Burp 的 Intruder 中,把 password 参数设为 payload 位置
-
用同一个验证码 ABC123,遍历密码字典
因为验证码没有失效,每个密码尝试都使用同一个验证码,所以 Intruder 可以正常工作——不会被“验证码错误”拦住。
6.4 用 Burp Suite 实操步骤
-
打开 Burp,代理拦截设为 On
-
在浏览器中输入正确的验证码,点击 Login
-
Burp 拦截到 POST 请求,发送到 Intruder

-
标记 password 参数为 payload 位置
-
设置 payload 为密码字典(常用密码列表)
-
保持 captcha_user 参数不变(就是那个正确的验证码)
-
点击 Start Attack

-
观察响应长度,哪个响应长度和其他的不一样,哪个就是正确的密码

6.5 成功登录
爆破出密码后,手动输入正确的账号密码和验证码,登录成功。
七、Medium 安全级别
7.1 尝试爆破
切换到 Medium,重复 Low 级别的爆破流程。
Medium 级别多了 isset($_SESSION["captcha"]) 检查,但核心逻辑没变——验证码验证通过后没有被销毁。

所以同样的方法可以绕过 Medium 级别。
7.2 源码分析
if(isset($_SESSION["captcha"]) && ($_POST["captcha_user"] == $_SESSION["captcha"]))
{
// 验证码正确,检查用户名密码
// … 但没有销毁验证码!
}
验证码验证通过后,$_SESSION["captcha"] 的值保持不变,所以可以复用。
八、High 安全级别
8.1 尝试爆破
切换到 High,同样的方法。
High 级别的逻辑和 Medium 一样,验证码同样没有被销毁。

结论:三种级别都存在同一个问题——验证码可重复使用。
8.2 为什么 High 级别也没防住?
因为 High 级别只是对输入做了更严格的过滤(在其他关卡中),但在这一关里,High 级别的验证码逻辑和 Medium 完全一样——验证码没有一次性失效。
这是一次性验证码 vs 会话验证码的区别。这一关用的是“会话验证码”(一个 Session 对应一个验证码),而不是“一次性验证码”(每次验证后销毁)。
九、应该怎么修复?
一个安全的验证码系统应该这样设计:
if($_POST["captcha_user"] == $_SESSION["captcha"])
{
// 验证通过,立即销毁验证码
unset($_SESSION["captcha"]);
// 然后检查用户名密码
if($_POST["login"] == $login && $_POST["password"] == $password)
{
// 登录成功
}
}
关键:验证码验证通过后立即销毁,这样每一次登录尝试都必须输入新的验证码。
十、真实世界:CAPTCHA 绕过案例
CAPTCHA 绕过在现实世界中非常常见,2024-2026 年依然大量出现:
CVE-2025-65807:Gila CMS 评论验证码绕过漏洞,攻击者可利用该漏洞绕过验证码进行 SQL 注入和 XSS 攻击。
CVE-2025-0366:WordPress Captcha 插件存在验证码绕过漏洞,CVSS 评分 8.1(高危),允许攻击者绕过验证码进行 SQL 注入等攻击。
CVE-2026-40839:开源 CRM 系统 ESPO CRM 存在验证码绕过漏洞,攻击者可结合 SQL 注入实现未授权数据窃取。
CVE-2025-5324:某 CAPTCHA 生成器存在逻辑漏洞,允许攻击者通过重放攻击绕过验证,结合 SQL 注入漏洞窃取数据。
CVE-2025-2671:某 Web 应用防火墙的 CAPTCHA 实现存在绕过漏洞,攻击者可通过修改 HTTP 头绕过验证,进行 SQL 注入。
启示:验证码只是增加攻击成本的辅助手段,不是核心防线。如果验证码逻辑有缺陷(如可重复使用、可重放、可自动识别),它就无法起到防护作用。
十一、总结
这一关的核心问题不在于密码强度,也不在于注入漏洞,而在于验证码的设计缺陷——验证码验证通过后没有被销毁,导致攻击者可以用同一个验证码反复尝试密码。Low、Medium、High 三个级别的验证码逻辑都没有解决这个问题,所以三个级别都存在漏洞。防御方案很简单:验证码验证通过后立即用 unset($_SESSION["captcha"]) 销毁,让每一次登录尝试都必须输入新的验证码。记住一句话:验证码的核心价值在于“一次性”,如果验证码可以重复使用,它就和没有验证码一样。
重要声明:本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。
如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享,也可以留言告诉我你遇到的其它问题,我会尽快回复。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。
网硕互联帮助中心





评论前必须登录!
注册