好久没碰DVWA了,最近有个朋友问我Brute Force模块到底怎么系统地刷,说自己照着网上的截图点了一通,Low级别都爆破得不顺利,High级别更是直接卡在Token获取上。我听完大概就知道问题出在哪了——大多数教程只给你看“成功的那一刻”,不会告诉你中间踩过的坑、绕过的弯,更不会解释每一步背后的逻辑。把DVWA的Brute Force模块从头到尾刷一遍其实花不了多少时间,但如果你只是想跑通一次,那收获会非常有限。这篇文章我打算换一种讲法,按照Low到Impossible四种安全等级逐个拆解,从抓包到脚本实现,从绕过思路到防御视角,把我自己在授权环境里反复调试的经验完整写出来。读完之后你不仅能爆破成功,还能把这一套逻辑迁移到真实业务系统的口令安全评估里。
DVWA是Damn Vulnerable Web Application的缩写,一个典型的Web安全靶场应用,跑在本地就能练手。Brute Force是它十二个漏洞模块里最基础、也最适合入门的一个,因为暴力破解本身不涉及复杂的注入语法或奇技淫巧,它本质上就是“不断尝试”四个字,但要把这个“不断尝试”做得高效、做得有技巧,需要理解HTTP请求、会话管理、CSRF Token机制,以及服务器端的防护逻辑。
1. 先搞懂DVWA的Brute Force模块到底在练什么
1.1 DVWA是什么,为什么要用它练手
DVWA是一个开源的PHP+MySQL靶场应用,集成了一堆常见的Web漏洞场景,SQL注入、XSS、文件上传、命令注入等等都有,Brute Force就是其中第一个。很多安全初学者在搭建好环境后做的第一件事,就是打开这个模块,注册一个普通权限的账号,然后对着登录表单开始琢磨怎么爆破成功。
为什么推荐用它练手?因为它的漏洞环境是“故意造出来”的,每一种漏洞都做了明确的等级划分,Low级别相当于毫无防护的裸奔状态,Medium级别加了一点聊胜于无的过滤,High级别开始模拟真实系统里比较常见的防御手段,Impossible级别基本相当于一个规范的安全实现。这种由浅入深的递进设计,恰好对应了暴力破解攻击从“瞎猜”到“绕过Token”再到“防御视角”的完整成长路径。
而且DVWA跑在本地,你可以随便折腾,不用担心把线上系统搞出问题,也不涉及真实用户数据。对于想在Web安全方向入门的人来说,它是一个非常干净、安全的练手环境。对已经有经验的人来说,DVWA的意义可能在更快的速度把某种攻击思路跑通,然后用很短的时间验证防御方案。
1.2 Brute Force模块的四种难度设计逻辑
Brute Force模块是一个登录表单,用户名密码通过POST方式提交,后端拿着输入去数据库里匹配,匹配成功就给一个会话。四种安全等级对这个表单的防护程度做了完全不同的处理。
Low级别的代码几乎没有任何防护,直接拼接SQL查询,也没有任何失败限制、延迟或验证码。这意味着你可以用工具一瞬间尝试几千个密码,或者手工用Burp Suite的Intruder慢慢跑。这个级别的核心意义在于让你先看明白暴力破解的基本流程:抓包、设置Payload位置、选字典、跑结果。
Medium级别增加了两个防护点:登录失败后强制sleep两秒,以及检查User-Agent请求头是否包含特定标识。sleep的作用是降低你的爆破速度,User-Agent检查则是想过滤掉一部分自动化工具的默认指纹。当然这些都是非常初级的手段,我们稍后可以用几秒钟绕过去。
High级别的防护就有点东西了——它引入了一个随机会话Token(在DVWA里叫user_token),每次打开登录页面时Token都不一样,提交登录请求时后端会校验这个Token,不匹配直接拒绝。这意味着你不能再简单地往Intruder里扔一个密码字典就开跑,你得先想办法从页面里把Token挖出来,再带着Token去爆破。这就是很多人卡住的地方。
Impossible级别基本就是安全编码范例了:PDO预处理查询、加盐哈希校验、登录失败后强制等待、多次失败直接锁定账号。到这一级,传统的暴力破解已经几乎没法生效,我们的练习目标也要从“怎么破解”转变成“如何借鉴它的防御思路”。
1.3 从代码层面看四种级别到底差在哪
我在反复调试这个模块的时候,把每一次请求、每一个响应都抓下来看,发现代码层面的差异导致了完全不同的攻击方式。下面列一下大概的逻辑,你不用记代码,理解思路就行。
Low级别的核心就是一个裸的SQL查询,表单提交后直接把用户名和密码拼进查询语句里,没有Token、没有延迟、没有锁定。这种代码放在真正的生产环境里,唯一的优点就是代码简单,缺点是不需要我说你也知道。
Medium级别的代码在查询前加了一个判断,如果用户名或密码为空就把脚本停下来,登录失败后让脚本延迟两秒,同时检查请求头中的User-Agent是不是从特定浏览器发出的。这里的逻辑是:如果是人肉操作,两秒延迟无所谓;如果是自动化工具,很多默认UA就会暴露身份。
High级别的代码在数据库查询之外引入了一个user_token字段,这个Token在GET请求返回页面的时候动态生成,被放在一个隐藏表单字段里。POST提交时,后端先校验Token是否匹配,再校验用户名密码。如果Token错了,无论密码对不对都会被拒绝。很多人在Low和Medium级别习惯了直接爆破,到High级别突然发现自己发出去的请求全是Token不匹配的错误,就是这个原因。
Impossible级别用了更复杂的机制:PDO预处理彻底防住了SQL注入;密码用加盐哈希存储,就算数据库泄露,也没法直接看到明文;登录失败后延迟三秒;同一用户名连续失败三次,账号直接进入锁定状态。这套组合拳下来,爆破的成功率趋近于零,除非你的密码字典里恰好有那个原始的弱口令,而且还得在三次尝试之内蒙对。
看完这四种级别的代码差异,你就会明白,暴力破解是否可行,完全取决于服务端有没有做好“限速、校验、锁定、隔离”这几件事。这也是这篇文章真正想让你学到的东西——每一次爆破,都是在给服务器端的安全策略做一次全方位的体检。
2. Low和Medium级别:从手工抓包到第一次自动化
2.1 环境准备与登录表单抓包
动手之前,先把环境理一遍。我的习惯是在本地跑一个LAMP或LNMP之类的集成环境,把DVWA解压到Web根目录,配置好数据库,然后正常访问首页初始化。整个过程不需要公网,也不需要做端口映射,纯粹本地练习,安全省心。
初始化完成后,先把自己登录进去。DVWA的默认账号密码是固定的,登录之后你还需要把安全等级切到Low。这里有个很多人忽略的细节:Brute Force模块里的“攻击对象”是DVWA自己的登录表单吗?不是。你访问Brute Force页面时看到的是一个独立的登录框,让你输入用户名密码,它把结果提交到后端去校验。这个登录框和DVWA本身的会话机制相互独立,但又共用同一个Cookie会话。所以在爆破之前,你先得手动访问一次这个攻击页面,拿到当前的Cookie,否则工具发出去的请求根本通不过DVWA自身的登录验证,会直接返回403。
实际测试时我习惯用Burp Suite来抓包。打开代理,浏览器访问DVWA的Brute Force页面,手动提交一次错误的用户名密码,然后在Burp里找到那条POST请求。这条请求长得大致是这样:
POST /dvwa/vulnerabilities/brute/?username=admin&password=test&Login=Login HTTP/1.1 Host: 127.0.0.1 Cookie: PHPSESSID=xxxxxxxxxxxx; security=low Content-Type: application/x-www-form-urlencoded可以看到参数是直接拼在URL上的,用的是GET方式,Cookie里带着会话ID和安全等级。很多人到这一步就开始往Intruder里塞字典了,但我建议先做一件事:把这条请求原样发出去几次,观察响应内容有什么特征。比如成功响应返回“Welcome to the password protected area”,失败响应返回“Login failed”。这个差异字符串就是你后面识别爆破成功与否的“指纹”。
2.2 Low级别爆破:Burp Suite Intruder完整配置
拿到请求包之后,在Burp里右键发送到Intruder。首先需要设置Attack Type,我用的方法是Sniper,因为你只需要爆破密码这一个位置,用户名可以固定为admin。如果你连用户名也不确定,可以换成Cluster Bomb,同时爆破用户名和密码,但那样组合数量会非常大,建议先把用户名固定下来,专心跑密码。
接着标记Payload Position。把password参数对应的值选中,点击Add标记成一个Payload位置。Payload类型选择Simple List,然后从外部加载一个密码字典。这里我要多说一句:字典的选择基本决定了你的爆破上限。如果你用的是几万条的通用弱口令列表,Low级别大概率能跑出来;如果你只放了几个常见密码,那就纯粹靠运气。
字典加载后,还需要调整请求频率。Intruder默认可以开很多线程,但我劝你态度端正一点——这不是让你去攻击别人的系统,而是在本地靶场练习。把线程数设置成1到5之间,加一个几百毫秒的请求间隔,既不会给本地服务造成压力,也容易看清楚响应变化。
设置好之后点击Start Attack,观察结果列表。Low级别的响应长度差异非常明显,错误密码的响应包长度基本一致,而正确密码的响应会多出一段欢迎语,长度明显不同。以我自己的经验,用一份几千条的弱口令字典跑Low级别,通常几十秒内就能出结果,时长取决于你的线程数和字典大小。
注意:Low级别的密码字段用的是明文GET提交,密码会直接出现在浏览器地址栏和服务器日志里。这个细节在真实环境里是很大的信息泄露点,但在靶场里正好能让初学者直观地看到“爆破的目标参数长什么样”。
2.3 Medium级别绕过:响应延迟与User-Agent校验
Medium级别一上来,很多人会发现自己原来跑得飞快的Intruder突然慢了半拍——每个请求都明显卡顿,响应时间暴涨。原因其实不复杂,如果你直接查看源码,会看到登录失败后有一行sleep(2),也就是说每次密码错误,服务端都会强制等待两秒再返回结果。一个几千条的字典跑下来,每个失败请求多花两秒,时间成本会非常吓人。
绕过的思路有两个方向。第一个方向是把请求间隔设成负数,利用并发请求让sleep效果被“填满”,但这样反而容易把本地服务拖慢,不推荐。第二个方向是理解sleep只会出现在“登录失败”的场景里,如果你用正确密码,后端就直接放行了。所以本质上你需要的只是一条能判断成功的请求,其他失败请求慢就慢了,它影响的是总时长,并不影响最终判断。
User-Agent校验也是Medium级别新增的一个点,它要求请求头里必须带有看起来像浏览器的UA。Burp Intruder发送请求时默认使用你自己的项目设置,这个UA大概率是没问题。但如果你用的是某些脚本工具,默认UA就容易暴露,直接被服务端拒掉。处理办法是手动在请求头里加上一个标准浏览器UA,比如:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36还有一个不确定性因素在Cookie:DVWA的session本身有过期机制,如果爆破时间太长,中间会话可能会失效,所有请求都变成403。所以爆破之前可以先在浏览器里重新加载一下页面,确认账号还处于登录状态,再开始跑。
2.4 如何判断爆破成功:不只是看状态码
很多新手爆破完之后,只看HTTP状态码来判断成功失败,这个习惯在DVWA的Brute Force模块里不太靠谱。因为无论密码对不对,请求返回的状态码大概率都是200,真正有区分度的是响应正文里的内容片段。
我自己的判断方法很简单。在Intruder的Options里设置一个Grep-Match规则,把失败提示的关键词“Login failed”标记出来,然后开始攻击。运行过程中,凡是响应里没有这个关键词的行,就是潜在的命中记录。然后再双击打开那条响应,确认是否包含“Welcome to the password protected area”。这种双保险的做法能有效避免因响应长度碰撞造成的误判。
还有一种情况:你以为自己爆破成功了,但密码其实是错的。这种乌龙通常出现在,你用了某个密码后页面弹出了类似“你已经尝试了太多次”的错误。DVWA Low级别的代码没有这个限制,但如果你自己改过源码,或者环境里装了额外的WAF插件,就不一定了。所以验证成功的时候,建议在响应正文里多检查两个关键点:是否包含欢迎词,以及是否出现账号锁定或频率控制的提示。
到这里为止,Low和Medium两个级别其实已经练完了暴力破解的基础操作,你也掌握了一个比较完整的判断流程:抓包、设置Payload、选字典、跑请求、靠区别特征判断结果。接下来我们要面对的是High级别这个“拦路虎”。
3. High级别:带着CSRF Token的爆破才是实战
3.1 为什么加了Token之后手工爆破就废了
High级别的代码里加了一个user_token字段,每次GET请求打开登录页面时,后端会生成一个随机Token,放进页面表单的隐藏字段里。你提交POST请求的时候,必须把这个Token原样带回去,后端校验Token和当前会话是否匹配,不匹配就拒绝。
这意味着什么?直观说,你在Intruder里固定好username和password两个位置不够了,因为你每次尝试都要先GET一次首页,从响应HTML里抓出最新的Token,再带着Token发POST请求。Token是一次性的、会话绑定的,同一个Token重复使用,后面的请求也会失败。
这背后的对抗思路是:爆破脚本必须“有状态”。它要像真实浏览器一样,先访问页面、解析表单、提取Token、再提交请求。很多第一次接触High级别的人,会试着从Low级别的请求包直接改,把Token参数随便填一个,结果发现所有响应都变成“CSRF token mismatch”,然后整个人就懵了。
我在本地调试这个逻辑时,第一反应是写一个Python脚本,使用爬虫处理会话状态,循环做两件事:GET提取Token、POST提交尝试。这个思路在本质上模拟了一个真实用户在浏览器里反复刷新页面、填表、提交的过程。
3.2 写一个“先取Token再爆破”的Python脚本
下面这个脚本是我在实际调通High级别时使用的简化版本,它使用requests库处理Cookie和Token抓取,逻辑很直接,适合入门练习时参考。脚本的核心思想很简单——每一次密码尝试之前,都先请求一次登录页面,用正则提取出隐藏字段的user_token,再把它和当前尝试的密码一起POST给服务器。
import re import requests import sys BASE_URL = "http://127.0.0.1/dvwa/vulnerabilities/brute/" COOKIES = { "PHPSESSID": "你的会话ID", "security": "high" } def get_token_and_cookies(): resp = requests.get(BASE_URL, cookies=COOKIES) match = re.search(r"name='user_token'\s*value='([^']+)'", resp.text) if not match: raise RuntimeError("无法获取 user_token,请检查登录状态和页面结构") return match.group(1) def try_login(username, password): token = get_token_and_cookies() payload = { "username": username, "password": password, "Login": "Login", "user_token": token } resp = requests.post(BASE_URL, data=payload, cookies=COOKIES) return resp.text if __name__ == "__main__": username = "admin" password_list = ["123456", "password", "admin", "letmein"] for password in password_list: html = try_login(username, password) if "Welcome to the password protected area" in html: print(f"[+] 爆破成功:{username}:{password}") sys.exit(0) else: print(f"[-] 尝试失败:{password}")跑这个脚本之前,先把你的Cookie填进去。你要做的是登录DVWA后打开High级别页面,然后用浏览器开发者工具或者Burp抓包找到PHPSESSID的值,手动填进去。脚本跑起来后,每尝试一个密码,它都会先访问一次页面拿Token,再提交请求,整个过程和真实浏览器行为几乎一致。
实际测试下来,你会发现每个密码的尝试时间取决于目标服务器的响应速度和sleep逻辑,High级别本身没有强制延迟,但Token的获取、请求的往返都会增加一点开销。这个脚本结构完全够用,如果你觉得速度太慢,可以预取一批Token,然后用一个请求队列去跑,但那是更高阶的玩法,不在这个入门练习的讨论范围里。
3.3 脚本中的几个关键实现细节
第一次写这个脚本的人大概率会在两个地方摔跟头。第一个是Cookie失效。我上面用的是手动填Cookie的方式,但Cookie本身有有效期,而且DVWA的会话会因为你反复登录、切换安全等级而变化。如果你脚本跑了一半突然所有请求都返回403,先别急着调代码,试试重新登录DVWA、刷新页面,替换Cookie再跑。
第二个是正则提取不到Token。页面里的user_token字段格式不一定固定,我在不同版本的DVWA里见到的写法就有差异。如果你用的版本和我的正则不匹配,建议先把页面HTML保存下来,肉眼确认隐藏字段长什么样,再调整正则。调试这种问题最快的方法是在脚本里加一行打印,把response.text的前几百个字符打出来,一眼就能看到底。
还有一个容易忽略的点:POST请求参数顺序不一定重要,但user_token必须和当前会话匹配。如果你用了并发或者多线程,两个线程同时取Token、同时提交,其中一个必定会因为Token已经被使用而失败。所以High级别爆破时,线程数建议老老实实设为1,串行跑,安全稳定。
另外一个细节是密码字典里的特殊字符。有些密码可能包含&、+、=这类URL保留字符,requests库的data参数会自动做URL编码,这一点比直接用Burp的原始包要省心,不容易因为编码问题导致参数截断。
3.4 爆破节奏控制与字典选择
High级别的爆破,本质上已经不是在“猜密码”了,而是在“模拟真实用户提交表单”的同时猜密码。在这种模式下,爆破节奏远比Low级别重要。如果你是模拟真实攻击,目标系统有账号锁定策略,你在三次错误后就该考虑换用户名或停止,而不是开个一百线程去怼,那等于是在敲锣打鼓地告诉管理员有人在爆破。
字典的选择也是这个道理。很多初学者喜欢下载一个几十G的“超全字典”,然后扔进脚本里慢慢跑。先说结论:在我个人的经验里,对DVWA这种教学环境,一个几万条的高质量弱口令字典,比一个几百万条的海量字典更出效果,因为靶场设计者通常会故意把弱口令藏在常见密码列表里,海量字典反而会把有效命中淹没在超长运行时间里。
我个人整理密码的时候有个习惯——分级。第一梯队放“默认密码”和“极简密码”,比如admin、123456、password、letmein、qwerty这类;第二梯队放“和用户名相关的变体”,比如admin123、admin@123、Admin123!;第三梯队才放通用弱口令。跑的时候先用第一梯队,目标系统若没有三次锁定限制,基本很快就出结果。
提示:在High级别练习时,每跑完一个密码,建议稍等片刻再继续,不要用“请求连发”的模式。这样既不容易触发服务端的限流逻辑,也方便你观察每个请求对应的Token获取情况。
4. Impossible级别:从攻击视角切换到防御视角
4.1 Impossible级别改了什么
等你能稳定跑通High级别之后,可以顺手把DVWA的安全等级切换到Impossible,再跑一次刚才的脚本。大概率你会发现,所有密码全部失败,甚至可能连请求的响应都变了。原因不复杂,Impossible级别做足了功课。
首先是PDO预处理查询。这意味着无论你输入什么用户名密码,后端都用参数化查询和数据库交互,不会被任何注入手法绕过。其次是密码哈希加盐,数据库里存的是一串不可逆的哈希值,就算密码字典里有一个弱口令,你也得先让后端拿它去做同样的哈希运算才能比对。换句话说,字典里必须有一个“恰好等于正确密码”的条目,爆破才有可能成功。
更关键的是,Impossible级别加入了失败延迟和账号锁定。同一用户名连续三次登录失败后,账号直接被锁定,后续无论密码对不对,都会被拒绝。这意味着传统的跑字典模式彻底失效,因为你的第三次尝试之后,无论字典里藏了多少正确答案,都没机会试出来了。
从攻击者视角看,这种防护组合基本宣告了“在线爆破”这条路走不通。但如果你换个角度思考,这不正是一个绝佳的防御范式吗?DVWA的Impossible级别几乎就是一份现成的安全编码清单,把每个防护点都明明白白摆在那儿了。
4.2 把这些防护措施搬到真实业务系统
我见过很多人在靶场里跑通了High级别,觉得“哇,暴力破解好酷”,转头就把这段经历写进简历里。但说实话,能跑通脚本的人很多,能说清楚为什么防护不住的人很少。暴力破解的学习价值从来不在攻击本身,而在于攻击结束之后,你能不能反过来把防护措施落地到真实业务系统。
在真实系统里,我总结下来至少要做这几件事:
第一,账号锁定策略一定要有,但锁定的粒度要设计好。全站锁定容易把自己打成拒绝服务,按用户名锁定或者按IP锁定会相对温和。DVWA的Impossible级别是连续失败三次锁账号,真实系统里可以做成五分钟内失败五次,然后封禁十五分钟,这个阈值需要根据业务场景调。
第二,登录请求必须要有CSRF Token。这个机制在DVWA里是为了增加爆破成本,在真实系统里更是基础安全要求,它能顺带解决掉很多自动化攻击面。Token必须和会话绑定、一次性使用、定期轮换。
第三,密码存储绝对不能用明文或者裸哈希。DVWA的Impossible级别用了加盐哈希,真实系统里建议使用更慢的密码哈希算法,比如bcrypt。慢不只是为了慢,而是让每一次密码猜测都付出计算成本,从根本上提高爆破的时间开销。
第四,失败的登录尝试一定要有审计日志。日志里要记录用户名、来源IP、时间戳、提交的数据。这些日志平时看着没用,一旦发生真实爆破事件,是你分析攻击来源、确认受损账号的唯一依据。
4.3 暴力破解攻击的检测思路
从蓝队视角看,暴力破解的检测逻辑其实很直白:正常用户不会在短时间内反复提交同一个表单几十次,更不会每次提交都换一个密码。你可以基于这个特征设计检测规则。
常用的检测策略有几种。第一种是频率检测,针对同一用户名的登录失败次数做统计,比如十秒内失败超过五次,直接触发告警。第二种是来源IP检测,同一IP在短时间内尝试大量不同用户名,可能是横向爆破。第三种是行为特征检测,比如某个请求头里的User-Agent很固定、请求路径高度一致、提交的密码长度分布异常,都可以作为辅助信号。
另外还有一个容易漏掉的点:分布式爆破。攻击者可能用大量IP轮流尝试同一个账号,这种情况下单IP维度不好判断,需要把用户名维度的失败次数也纳入统计。这个检测逻辑结合DVWA练习的经验,你会有更深的体感。
我在实际做安全评估的时候,经常用一套组合拳来验证暴力破解防护是否真的有效:先用Burp跑少量密码,观察响应是否有延迟变化;再尝试连续多次失败,看账号是否被锁定;然后换一个IP来源,看同一账号是否还能继续尝试;最后检查日志,确认每一次失败尝试都被记录到了。这个过程几乎就是DVWA从Low到Impossible的“反向操作”,你在靶场里练过的每一步,都能对应到真实防御评估的某个环节。
5. 常见问题与排查技巧实录
5.1 爆破中遇到403和404先查这两件事
403在DVWA的Brute Force练习里是出现频率最高的错误。如果你发出去的请求全部是403,第一件事是检查Cookie里的会话ID还有没有效、会话状态是不是已经被重置。第二件事是检查你的请求头是否齐全,特别是Referer和User-Agent。DVWA某个模块对请求头校验比较严格,Referer缺失或者和Host不一致,会被直接拒绝。
404相对少见,但也不是没有。原因通常是URL路径写错了,比如漏掉了vulnerabilities/brute/这一层目录,或者把模块名写成了复数。如果在浏览器里能正常打开爆破页面,但在脚本里总是404,优先检查路径拼接是否用了绝对路径,避免相对路径解析错误。
5.2 响应全部一样长怎么办
这是新手最容易踩的坑:爆破跑完了,Intruder列表里所有响应长度一模一样,根本看不出哪个是成功的。出现这种情况,大概率是你判断成功的标志选错了。
Low级别里,正确密码的响应会多出一段欢迎语,所以响应长度一定不同。但如果你请求的是某个API接口、或者目标页面返回的是一个固定模板、数据区域不在响应正文里,长度可能就完全一样。这时候不要只盯着长度看,要配置Grep-Match,用关键词匹配来判断。DVWA里成功响应的关键词是“Welcome”这类固定文本,你在Intruder的Options里把这个词加进去,运行后看对应列的打勾状态就行。
另一个可能性是:你根本没有爆破成功,字典里没有正确密码。这种情况下响应自然全部一样长。先把字典换成已知的正确密码试一次,确认能返回欢迎语,再重新跑爆破。
5.3 速度太慢、被封IP、会话失效的处理
速度慢的原因有很多,Low级别通常是你把线程数设得太低,Medium级别大概率是sleep干扰,High级别则是每个请求前都要额外GET一次Token。针对这三种场景,解法并不相同。
Low级别直接调高线程数就行,本地靶场环境开二十个线程不会有压力。Medium级别想提速,可以接受它慢一点,或者固定并发,等待时间分摊到多个线程里。High级别的提速空间有限,因为Token获取是串行依赖,但你可以预生成一批Token,然后用一个线程池去并发提交,代价是请求日志里会出现更多Token校验失败的情况,需要额外处理。
“被封IP”在本地DVWA里不太会发生,但如果你自己加了WAF插件或者限制频率的模块,就会看到类似IP被临时拒绝的响应。此时最快捷的处理办法是重启Web服务的会话状态,或者等封锁时间过去。
会话失效是另一个让人头疼的问题,高发于低安全等级下跑大字典的场景。DVWA的PHPSESSID引用了一个持久的数据库会话,如果长时间没操作,后端会认为会话过期。我的建议是每次大批量尝试之前,先手工在浏览器里刷新一次爆破页面,然后把新的Cookie填进脚本或者Burp里。把任务拆成多个小批次跑,反而比一次性跑完更稳。
5.4 一份实用的问题速查表
| 现象 | 优先排查项 | 解决办法 |
|---|---|---|
| 全请求403 | Cookie失效 / 未登录 | 重新登录DVWA,更新Cookie |
| 全请求404 | 路径拼接错误 | 检查脚本或Intruder里的URL路径 |
| 响应长度一致 | 成功标志选择错误 | 改用Grep-Match,检查关键词 |
| Intruder里UA异常 | UA被后端拒绝 | 手工指定浏览器User-Agent头 |
| 每个请求都很慢 | sleep延迟 / Token获取开销 | 调线程数、接受延迟、优化Token获取 |
| 脚本中途会话失效 | PHPSESSID过期 | 重新登录,刷新Cookie |
| 无法获取user_token | 页面格式不匹配 | 保存HTML,调整正则表达式 |
| 爆破成功但无法登录 | 账号锁定或会话异常 | 检查是否触发锁定,切换等级重置 |
在靶场里刷DVWA的Brute Force模块,看起来只是从Low级别一路爆破到Impossible级别,但真正有价值的收获在于你想明白了每一层防护为什么会让攻击变得困难。我个人刷完这个模块最大的感受是:暴力破解不是一个“技术含量很高”的攻击方式,但它像一面镜子,能照出目标系统在认证链路、会话管理、限流锁定和日志审计上的所有短板。把这些短板一个个补齐,比单纯掌握几个爆破工具重要得多。后续如果你有兴趣,可以继续把DVWA的SQL注入、XSS、命令注入这些模块按照同样的思路过一遍——你会发现,每种漏洞攻击模式和防御思路之间,都有非常清晰的对应关系。