1. 疑问与原理解析
疑问:代码中使用了 set_token(),每次登录失败都会刷新 Token,并且验证逻辑是在后端进行的 (if($token == $_SESSION['token']))。既然 Token 是动态随机的,且每次只能用一次,为什么还能爆破?
原理 :确实,这种机制有效地防止了 多线程并发爆破 (因为 Token 是一次性的,线程 A 用了,线程 B 的就废了)。但是,它 无法防止单线程的递归爆破。
因为服务器为了让正常用户能继续尝试登录,必须在 响应包(Response)的 HTML 表单中返回一个新的 Token:
<input type="hidden" name="token" value="s8798df7s98d7f98s7df..." />
攻击工具(如 Burp Suite)可以模拟浏览器的行为:“看一眼上次回包里的新 Token,把它填进这次的请求包里”。
2. 渗透测试过程 (Burp 高级操作)
这一关不能用普通的 Intruder 模式,必须配置 递归提取 (Recursive Grep)。
第一步:抓包与模式选择
- 随便输入账号 test、密码 test,抓包。
- 发送到 Intruder。
- Attack Type (攻击模式):必须选择 Pitchfork (草叉模式)。
- 为什么?因为需要同时控制两个变量:
password(字典)和token(上一包的返回值),且它们是一一对应的关系。
- 为什么?因为需要同时控制两个变量:

第二步:设置变量位置
我们需要标记两个位置:
password的值。token的值。
示例:
password=§test§&token=§38649789...§
第三步:配置 Payload (重头戏)
Payload Set 1 (对应 password):
- Type:
Simple list - Load: 载入密码字典。

Payload Set 2 (对应 token):
- Type: 选择 Recursive Grep (递归 grep)。
- 配置:
- 下面会让你选从哪里提取,现在先不管,去设置 Payload Setting。
- 注意 :在 Payloads 界面下方,有一个
Initial payload for first request(第一次请求的初始 Payload), 这里必须手动填入你刚才抓包时那个原始 Token。没有初始 Token,第 1 个包就会因为空 Token 报错,导致整个链条断裂。

第四步:设置提取规则 (Settings 选项卡)
切到 Settings 选项卡,找到 Grep – Extract (Grep 提取) 区域。
- 勾选
Extract the following items from responses。 - 点击 Add。
- Burp 会弹出一个框,点击 Refetch response,去 Response 响应包里选。找到那行
<input type="hidden" name="token" value="..." />。 - 选中
value引号里面的那串 Token 字符串。 - Burp 会自动生成正则表达式。点击 OK。
- 回到 Payloads 选项卡,确认 Payload Set 2 已经自动关联了这个提取规则。


第五步:设置单线程 (至关重要!)
因为 Token 是一次一换的,第 2 个请求必须等第 1 个请求拿到 Token 才能发。
- 在 Resource Pool (资源池) 选项卡中。
- 创建一个新的资源池。
- Maximum concurrent requests (最大并发请求数):设置为 1。
- 如果不设为 1,多线程会导致旧 Token 被抢占,爆破直接失败。

第六步:开始攻击
点击 Start Attack。会发现攻击速度比以前慢(因为是单线程),但 Token 每次都在变,且每次都能成功绕过 Token 校验,看好对应关系,会发现下个包中的 token 值,就是上个包中的 token 返回值,直到爆破出正确密码。

3. 代码审计
查看源码 bf_token.php:
// 后端验证
if($token == $_SESSION['token']){// ... 验证账号密码 ...} else {$html.= '<p> csrf token error</p>';}
// 漏洞点:在验证结束后,生成了新的 Token,并且在后续的 HTML 中输出了。set_token();
// ...
<input type="hidden" name="token" value="<?php echo $_SESSION['token'];?>" />
结论 :
Token 机制本身主要用于防御 CSRF(跨站请求伪造),它的设计初衷并不是为了防御 暴力破解 。
虽然它客观上增加了爆破的难度(强制单线程,降低速度),但并没有从根本上解决暴力破解的问题。
4. 修复方案 (防爆破的正确姿势)
既然 Token 防不住,那怎么防爆破?
- 验证码:加一个图形验证码(Token 是机器看不见的,但验证码需要图像识别)。
- 账号锁定:连续失败 5 次,锁定账号 30 分钟。
- IP 风控:同一 IP 请求频率过高,直接封禁。
- 延时响应:每次失败后,sleep(2) 两秒再返回结果,拖慢爆破速度。