1. 疑问与原理解析
疑问:代码中使用了 set_token(),每次登录失败都会刷新 Token,并且验证逻辑是在后端进行的 (if($token == $_SESSION['token']))。既然 Token 是动态随机的,且每次只能用一次,为什么还能爆破?
原理 :这种机制虽然有效地防止了 多线程并发爆破 (因为 Token 是一次性的,线程 A 用了,线程 B 的就废了)。但是,它 无法防止单线程的递归爆破。
因为服务器为了让正常用户能继续尝试登录,必须在 响应包 的 HTML 表单中返回一个新的 Token:

<input type="hidden" name="token" value="s8798df7s98d7f98s7df..." />
攻击工具可以模拟浏览器的行为:“看一眼上次回包里的新 Token,把它填进这次的请求包里”。
2. 渗透测试过程 (Burp 高级操作)
这一关不能用普通的 Intruder 模式,必须配置 递归提取 (Recursive Grep)。
第一步:抓包与模式选择
- 随便输入账号 test、密码 test,抓包。
- 发送到 Intruder。
- Attack Type (攻击模式):必须选择 Pitchfork (草叉模式,一一对应 (多对多并行),请求总数取决于最短的那一个 Payload 列表的行数)。
- 为什么?因为需要同时控制两个变量:
password(字典)和token(上一包的返回值),且它们是一一对应的关系。
- 为什么?因为需要同时控制两个变量:

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

Payload Set 2 (对应 token):
- Payload Type: 选择 Recursive Grep (递归 grep)。
- 它的作用是:“拿上一次请求的响应结果,作为下一次请求的 Payload。”
- BP 的普通模式是一股脑发包,或者并发发包,它不知道每次请求需要带上一次服务器给的新 Token,导致所有请求失效。
- 使用了 Recursive Grep 之后:
- 请求 1 发送。
- 响应 1 回来,BP 用“正则提取”从响应包里把 Token 抠出来。
- BP 自动把 token 填入 请求 2 的 Token 位置,发送。
- 响应 2 回来,BP 再抠出新 Token,填入 请求 3…
- 配置:
- 下面会让你选从哪里提取,现在先不管,去设置 Payload Setting。
- 注意 :在 Payloads 界面下方,有一个
Initial payload for first request(第一次请求的初始 Payload), 这里必须手动填入你刚才抓包时那个原始 Token。没有初始 Token,第 1 个包就会因为空 Token 报错,导致整个链条断裂。

第四步:设置提取规则 (Settings 选项卡)
切到 Settings 选项卡,找到 Grep – Extract (Recursive 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) 两秒再返回结果,拖慢爆破速度。