1. 核心原理
前端验证(Client-side Validation)仅能提高用户体验(防止用户输错格式),绝对不能 作为安全防护措施。因为攻击者完全控制客户端(浏览器),可以随意修改、禁用或绕过前端代码。
本关的漏洞核心在于:
- 校验仅在前端:验证码的正确性完全由浏览器端的 JavaScript 校验。
- 后端缺失校验:服务器接收请求时,完全没有检查验证码字段。
- 爆破可行性 :既然验证码是在前端生成的,且后端不查。那么只需要在浏览器里输入 一次 正确的验证码,骗过前端 JS 的拦截,成功发出数据包。之后在 Burp Intruder 中,无论重发多少次这个包,验证码参数都可以保持不变,服务器依然会照单全收。
2. 漏洞演示 (建议展示两种方法)
方法一:物理移除法
- 操作:在 Chrome 设置里禁用 JavaScript(Ctrl+P,再搜索 JS),或者使用插件(如 SwitchyOmega 旁的 JS 开关)一键禁用。
- 现象:验证码输入框直接消失,或者点击登录时不再弹出“验证码错误”的提示框,直接把包发给了服务器。
- 局限性:现代单页应用(SPA,如 Vue/React 写的)禁用 JS 后会白屏。
方法二:中间人绕过法(Burp Suite)
第一步:突破前端拦截
直接在输入框中填入 错误 的验证码,点击登录,发现抓包工具没有任何反应。
原因:HTML 中的 onsubmit 事件触发了 JS 校验,发现错误直接 return false,请求根本没有发出。
第二步:获取有效数据包
为了拿到能用于爆破的数据包,必须先“顺从”前端逻辑:
- 在浏览器中填写 正确 的验证码。
- 填写任意用户名和密码。
- 开启 Burp Suite 拦截,点击登录。
- 成功抓取到 POST 请求。
第三步:实施暴力破解
将抓到的包发送至 Intruder 模块。
- 攻击载荷:仅对 password 字段设置变量 §。
- 验证码处理 : 保持 vcode 字段为刚才输入的那个正确值不变。
思考:为什么验证码不需要变?因为这是同一个 HTTP 请求的反复重放。此时已经绕过了浏览器的 JS 环境,直接与服务器对话。而服务器根本不验证 vcode,所以这个旧的验证码就像一张“永久门票”。
第四步:攻击结果
载入字典开始攻击,通过观察响应长度(Length),成功爆破出正确密码。
3. 代码审计
前端代码 (HTML/JS):验证码的生成、展示和比对,全部 是在客户端通过 JavaScript 完成的。
// 生成验证码的逻辑,完全在本地运行
var code;
function createCode() {
code = "";
var codeLength = 5;
// ... 生成随机字符 ...
checkCode.value = code; // 显示在页面上
}
// 校验逻辑
function validate() {var inputCode = document.querySelector('#bf_client .vcode').value;
if (inputCode.length <= 0) {alert(" 请输入验证码!");
return false;
} else if (inputCode != code) {
// 仅仅是在本地比对 inputCode 和全局变量 code
alert(" 验证码输入错误!");
createCode(); // 输错了就刷新
return false; // 阻止表单提交
}
else {return true; // 只有这里返回 true,表单才会发给服务器}
}
分析:这段代码只能阻挡正常用户的手误,无法阻挡通过 Burp Suite 直接发包的黑客,因为黑客可以直接绕过浏览器执行环境。
后端代码分析 (PHP):这是漏洞的根源所在。请注意下面的 PHP 处理逻辑
if(isset($_POST['submit'])){
// 判断用户名密码是否为空
if($_POST['username'] && $_POST['password']) {$username = $_POST['username'];
$password = $_POST['password'];
//【高危漏洞点】// 这里直接开始拼接 SQL 语句查询数据库
// 完全没有获取 $_POST['vcode'],也没有进行任何验证码比对!$sql = "select * from users where username=? and password=md5(?)";
$line_pre = $link->prepare($sql);
$line_pre->bind_param('ss', $username, $password);
if ($line_pre->execute()) {// ... 登录成功逻辑 ...}
}
}
分析:服务器端代码完全缺失了对 vcode 的校验逻辑。这意味着,HTTP 请求中是否存在 vcode 参数,或者该参数的值是什么,服务器根本不关心。
4. 修复方案
正确的做法是:
- 验证码的生成和校验必须在 服务端 完成。
- 前端仅负责展示和传输用户输入。
- 后端校验通过后,立即销毁当前验证码(防止复用)。
正文完
