1. 项目概述与核心内容拆解
1.1 DVWA是什么?为什么拿它练手
DVWA,全称Damn Vulnerable Web Application,翻译过来就是“该死的脆弱Web应用”。它是一套开源的Web安全靶场,专门用来给安全学习者、渗透测试工程师、开发人员练习漏洞挖掘和攻击复现。我第一次接触DVWA是在几年前的一次内部安全培训上,当时导师让我们用它来学习常见Web漏洞原理,从那以后它就成了我项目实战前最常用的“陪练沙箱”。
暴力破解(Brute Force)是DVWA里面最基础的漏洞模块之一。它的原理说白了就是“无限试错”——攻击者不依赖任何逻辑漏洞,不讲技巧,就拿用户名和密码字典一遍又一遍地尝试登录,直到撞出正确的凭证。生活化理解就是:有人把钥匙忘在家里,他不找开锁师傅,而是拿一大串钥匙挨个试门锁,总有一把能打开。
在真实的Web安全测试场景中,暴力破解攻击虽然“不优雅”,但效果非常稳定。它的成功率取决于两个关键因素:登录页面有没有防护机制(验证码、锁定策略)、用户的密码够不够强壮。DVWA把这个场景浓缩成几个等级,从完全裸奔的LOW到带token防护的HIGH,正好覆盖了从“脚本小子”到“专业选手”的进阶路线。
这个项目适合刚接触Web安全的新手,也适合准备做红队演练的初级渗透测试员。新手可以从这里理解“如何用工具发起攻击”“什么叫字典”“token如何绕过”,老手也能从MEDIUM、HIGH级别的防护绕过思路里找到一些实战灵感。我后面所有的演示,都基于本地靶场环境,绝不涉及任何未授权的真实目标。
1.2 项目要解决的三个核心问题
暴力破解这个模块看似简单,但真正深入之后,你会碰到三条关键的知识链路:第一,怎么高效地发起爆破请求——这里要用到Burp Suite的Intruder模块或者自写脚本;第二,怎么理解目标接口的参数构造——DVWA页面背后实际提交了哪些数据、Cookie如何维持会话;第三,怎么应对不同级别的防护——从LOW的无防护,到MEDIUM的简单延迟,再到HIGH的token随机化,每种防护都有对应的绕过策略。
围绕这三条链路,我把整个实操拆成了“环境搭建—基础爆破—进阶绕过—防护反制”四个环节。任何一个人跟着这套流程走下来,至少能学会:
- 搭建一套完整的DVWA靶场环境
- 使用Burp Suite抓包、发送爆破请求
- 看懂字典文件的构造逻辑,知道弱口令为何一爆一个准
- 理解CSRF token对爆破的影响以及绕过思路
- 从攻击者视角反向推导,知道什么样的登录接口才算“安全”
这个项目最大的价值不在于你爆破成功了几个账号,而在于你完整经历了“观察—分析—尝试—绕过”这一整套安全测试思维训练。有了这个底子,后面学SQL注入、XSS、文件上传,思维路径都是相通的。
2. 环境准备与工具选型解析
2.1 本地靶场环境的完整搭建
手里没有靶场,一切都是纸上谈兵。我推荐用组合方式搭建:宿主机装Windows或Linux,用Docker跑DVWA容器,Burp Suite装在宿主机上做抓包和爆破工具。这套方案干净、可复现,想重置环境随时重建容器,不用怕把系统搞坏。
以下是基于常见实践的操作流程,Docker方式整体耗时大概5分钟:
# 拉取DVWA镜像,某些环境网络不稳时可用镜像加速 docker pull vulnerables/web-dvwa # 运行容器,将80端口映射到宿主机的8080端口 docker run -d -p 8080:80 vulnerables/web-dvwa # 访问 http://localhost:8080 初始化数据库浏览器打开页面之后,点击“Create / Reset Database”按钮,DVWA会自动建库、插入测试数据。登录用的默认账号是admin,密码是password,登录后会自动跳转到模块选择页面。
有个容易踩的坑:默认DVWA的PHP配置里allow_url_include必须是On,否则部分模块不可用。进入容器修改也很快:
docker exec -it <容器ID> bash # 找到 /etc/apache2/php.ini 或 /var/www/html/config/config.inc.php,确认相关配置如果手头没有Docker环境,退而求其次用phpStudy或XAMPP搭一套LNMP环境手动放DVWA源码也行,只是要自己处理PHP版本兼容问题。个人推荐还是Docker,省掉各种“我这环境怎么起不来”的破事。
2.2 爆破工具到底怎么选:Burp Suite vs 脚本
暴力破解的工具并不只有一种。我平时最常用的搭配是Burp Suite Community版做流量分析和简单爆破,Python脚本处理需要高并发、复杂绕过的场景。两者各有用处,并非替代关系。
Burp Suite的Intruder模块适合“快速验证”,在图形界面里配置请求模板、字典、线程数,点个按钮就能出结果。它的优势是可视化:你能看到每一个请求的状态码、响应长度、跳转方向,爆破成功的条目在长度或状态码上往往一眼就能分辨。
Python脚本适合“需要灵活逻辑”的场景。比如DVWA的HIGH级别带了token,每个请求必须先从响应中提取token再带入下一个请求,用Burp的时候就得到扩展里处理。而你用Python写for循环,加一个正则表达式提取token,逻辑就非常直观:
import requests import re # 先GET登录页拿到session和token s = requests.Session() r = s.get("http://localhost:8080/login.php") token = re.search(rb"user_token' value='(.*?)'", r.content).group(1).decode() # 然后POST登录请求 data = {"username": "admin", "password": "password", "user_token": token, "Login": "Login"} header = {"X-Forwarded-For": "127.0.0.1"} r = s.post("http://localhost:8080/login.php", data=data, headers=header) print(r.status_code, len(r.content))这段代码的原理很简单:requests库维护Cookie会话,先GET页面获得session与token,再带着token提交登录。代码虽短,但这就是爆破脚本最核心的骨架,后面加循环、加字典就是在这个基础上扩展。
从我的经验看,新手建议先从Burp Suite的图形化操作入门,理解请求和响应的对应关系,再切换到脚本方式提速。图形化让你知道“爆破请求长什么样”,脚本让你知道“爆破的逻辑怎么实现”,两条腿走路最稳。
2.3 Cookie、会话与请求结构的基础认知
不管是Burp还是脚本,爆破的本质都是不停发送HTTP的POST请求。所以你得先弄明白DVWA登录请求的结构。用Burp打开代理拦截,在登录页随便输个用户名密码提交一次,就能看到这样一条请求:
POST /login.php HTTP/1.1 Host: localhost:8080 Cookie: PHPSESSID=你的会话ID; security=low Content-Type: application/x-www-form-urlencoded username=admin&password=123456&Login=Login这里面有两个关键点:第一,Cookie里的security字段标明了当前安全等级,低等级是security=low,切换DVWA页面等级时这个值会变;第二,登录参数是username和password,提交按钮是Login=Login。Burp里抓到的这些原始内容,就是Intruder配置爆破模板的基础。
会话保持也不能忽视。PHP用PHPSESSID标识会话状态,爆破过程中如果请求没有携带这个Cookie,服务端会认为是未登录的匿名访问,结果可能全部404或302。Burp Intruder的请求模板会自动带着抓包时获得的Cookie,只要你不勾选某些失效选项,一般没问题;Python脚本里用Session对象也能自动处理Cookie。
理解了这三层基础——环境、工具、请求结构——相当于有了“作战地图”。接下来就是真刀真枪的打靶环节。
3. LOW级别暴力破解:从零到抓取凭证
3.1 第一轮实操:Burp Intruder完整爆破流程
LOW级别的DVWA登录接口没有任何防护机制,服务端唯一做的事就是把提交的用户名密码放到SQL查询里比对,没有验证码、没有锁定、也没有延迟限制。这是我教学生时最常用来讲“爆破最基础形态”的教学环境。
实际操作分五步走。第一步,打开Burp Suite,确认代理监听在127.0.0.1:8080,浏览器走代理;第二步,访问DVWA登录页,随便输一个不存在的用户名和密码,比如test/123456,提交时Burp抓到这个登录请求;第三步,右键请求发送到Intruder,设置攻击类型为Sniper(单一字典位置)或Cluster Bomb(多字典组合位置);第四步,把password=123456这个值标记为payload位置;第五步,导入字典、设置线程、开始爆破。
这里有个关键细节:LOW级别的服务端对用户名和密码的校验分开处理,用户名不存在时响应里会提示“Username does not exist”,存在但密码错误时提示“Login failed”。我见过很多新手只爆破密码不爆破用户名,结果密码字典再猛也撞不开门。正确的做法是先爆用户名,确认了有效用户名后再对密码位发起爆破,效率能翻倍。
爆破完成后,看Intruder的结果列表,有个很实用的判断技巧:按响应长度排序,正常情况下错误登录的响应长度都差不多(比如1485字节),爆破成功的那一条因为页面包含了欢迎语句或跳转逻辑,响应长度会明显不同。我常跟人讲:别一个个看状态码,直接找“响应长度离群值”,那是成功请求最显眼的特征。
3.2 字典选择的讲究:什么样的密码榜上有名
字典是暴力破解的弹药。LOW级别虽然服务器不设防,但如果字典里根本没有正确密码,爆一万次也是白搭。DVWA官方自带了一个密码字典,路径大致在/usr/share/wordlists或者项目目录下的password.txt,里面收了一批常见弱口令,比如123456、password、qwerty、letmein这类。
我平时实践时不会只用一个字典,往往把几类来源叠加起来:内置字典作为基础,再把自己收集的本年度Top100弱密码追加进去,如果目标环境带有中文用户习惯,再补上一批拼音密码(如zhangsan123)。选字典的标准只有一个:覆盖常见组合,不发散到不可能的范围。一个1000行左右的字典足够应对大多数靶场。
燃烧线程的设置也有学问。LOW级别没有防爆破机制,我习惯把线程设在10到20之间,既能快速跑完,又不会因为自己的代理或浏览器崩溃而中断。线程太高反而容易让Burp的Intruder卡住,输出窗口一片红,看起来像攻击成功了,其实只是网络阻塞。
爆破成功之后,Burp响应搜索关键词Welcome或You have logged in即可定位。实测下来,常见弱口令字典爆破DVWA自带账号,几乎都是几分钟内完成的事情。这也从反面说明了弱口令的可怕:不是攻击者多会爆,而是用户密码太配合。
3.3 第一轮小结:从LOW级里学到的实战经验
打完LOW级别,最直观的收获是两块:一是通过工具操作理解了HTTP请求与响应的结构;二是建立了一个判断爆破结果的分析框架——看状态码、看响应长度、看跳转位置、看关键词。
还有一个容易被忽视的细节:DVWA每个等级切换之后,登录接口实际还是同一个login.php,但服务端校验逻辑不同。这意味着你在LOW级别爆破成功拿到的会话,在切换到MEDIUM级别之后并不会自动继续生效。每次切换等级,都建议重新登录一次,别在这个上面消耗大量无用功。
4. MEDIUM与HIGH级别的防护绕过思路
4.1 MEDIUM级别:睡眠延迟与简单防护的局限
切换到MEDIUM级别之后,最明显的变化是服务端在登录验证失败后加了一段延迟逻辑,原理是调用sleep(2),让每次请求在服务端挂起两秒再返回。这个设计目的是减缓爆破速度:一个字典1000条,每条多2秒,整个跑完至少半小时,攻击者耐心就被消耗掉了。
不过这种防御方式在真正的攻击者看来,顶多算“减速带”,算不上“墙”。我实际测试时直接用了两条思路绕过。第一条是并发爆破:延迟只作用于服务端单次请求,客户端完全可以通过多线程同时发送大量请求,让10个请求同时在服务端排队睡2秒,总耗时依然是2秒而不是20秒。Burp的Intruder把线程数调到15,实测速度没有明显衰减。第二条是直接在Python脚本里用ThreadPoolExecutor发并发请求,代码结构也很简单:
from concurrent.futures import ThreadPoolExecutor def try_login(password): data = {"username": "admin", "password": password, "Login": "Login"} r = s.post("http://localhost:8080/login.php", data=data) return password, r.status_code, len(r.content) with ThreadPoolExecutor(max_workers=10) as pool: results = pool.map(try_login, password_list)所以MEDIUM级别的启发是:服务端要想真正抵御爆破,不能只靠延迟,还得加上验证码、失败锁定、IP信誉拦截等组合拳。单点防御永远是脆弱的。
4.2 HIGH级别的杀手锏:token随机化与绕过三板斧
HIGH级别把防护等级拉到了很多实际站点的水准:每次登录请求都需要带上一个user_token,这个token由服务端随机生成、绑定当前会话,一旦用过就失效。攻击者如果直接拿LOW级别的爆破模板发请求,服务端会校验token不匹配,直接拒绝响应,爆破请求全部无效。
我当时第一次碰到这个级别,也被卡了十几分钟,后来想通了原理之后其实很简单:token在每次GET登录页面时都会刷新,所以爆破脚本必须在每一次POST之前先GET一次,从响应页面的隐藏表单字段里提取新的token,再拼到POST请求里。这就是所谓的“先取token再登录”循环。
具体流程我用Python实现过,关键代码是提取和拼接两步:
import re, requests s = requests.Session() for password in passwords: page = s.get("http://localhost:8080/login.php") token = re.search(r"user_token' value='(.*?)'", page.text).group(1) data = { "username": "admin", "password": password, "user_token": token, "Login": "Login" } resp = s.post("http://localhost:8080/login.php", data=data)虽然每次爆破变成了“GET+POST”双请求,但完全的等待时间并没有增加很多,加上多线程并发,HIGH级别照样可以爆破成功。有些资料里提到用Burp的宏功能处理token,这在图形化界面上确实可以,但配置起来比较麻烦,对新手不友好,我建议直接上手Python脚本,逻辑反而更透明。
必须补充的一个细节是:因为每次GET都会刷新token,所以多线程共享同一个Session时要加锁,否则A线程拿到的token可能已经被B线程用掉,导致大量请求失败。我当时就吃过这个亏,加了threading.Lock()才解决。这个坑属于“不会写在任何官方文档里但实战必踩”的经典问题。
4.3 防护绕过背后的统一思路
从LOW到HIGH,防护级别虽然层层加码,但绕过逻辑有一条主线:先分析防护措施作用于请求链路中的哪个环节,再决定在哪个环节做对抗。睡眠延迟作用于服务端处理层面,就用并发请求对冲;token校验作用于请求参数层面,就在请求生成阶段模拟出合法token。
这种“分析—定位—对抗”的思路,在真实渗透测试中同样适用。很多时候安全测试最耗时间的不是爆破本身,而是理解目标前端页面的交互逻辑。工具永远是执行层,思考层才决定你能走多远。
5. 常见问题与排查技巧实录
5.1 爆破请求发出去没反应,先查代理和会话
我遇到过不少同样的问题:Burp的Intruder跑得好好的,但爆破结果没有一个成功。排查步骤是有固定顺序的。
第一,确认浏览器的代理配置。Burp作为代理工具,如果浏览器没走代理,Intruder连目标都发不出去。第二,确认请求里是否带了有效的Cookie。除了安全等级标识之外,登录请求必须带PHPSESSID,否则服务端可能直接拒绝。第三,确认POST参数名是否匹配。DVWA登录表单字段是username、password、Login,如果某些浏览器插件自动填充了额外字段,服务端可能校验出错。第四,看结果列表里是否大量出现302或403。如果出现302,一般是登录成功后跳转到index.php,这反而是成功的信号;大量403则可能触发了服务端WAF或IP限制。
这个排查思路对任何Web漏洞测试都通用:“先客户端、后服务端、再数据本身”是我调漏洞的三板斧。
5.2 响应长度看不出差异?加一项响应内容关键词判断
爆破成功判断最常用的方法是响应长度对比,但有些场景下服务端对“登录成功”和“登录失败”的响应长度几乎没有差别,比如返回的都是同一个空壳页面,只是局部提示不同。这时候再盯着长度就不灵了。
我的习惯是直接在Intruder的Grep-Match配置里加上几个关键词,比如Welcome、Logout、login.php?failed=true。爆破完成后,哪个请求的响应里命中了这些关键词,一目了然。相当于给爆破结果加了个过滤器,比肉眼看几百条响应高效太多。
如果你用的是Python脚本,判断逻辑就更直观了:响应文本里搜Welcome,搜到就打印密码并break,一分钟爆破完上千条字典毫无压力。
5.3 爆破了半天没结果,多半是字典和条件没对上
排除掉工具问题后,剩下的问题通常在字典本身。我遇到的最常见情况是:字典文件编码不对,里面的密码带\r(Windows换行符),提交到服务端后密码里多了个回车符,自然永远匹配不上。解决办法是在脚本里做一次strip:
password = line.strip()还有一个隐藏问题:DVWA的默认测试用户名是admin,但有些人用自定义字典爆admin时把大小写写错了,或者字典里包含了空行、注释行,导致大量的无效请求。写脚本时建议加个判断,跳过空行和以#开头的行,省下的时间也是时间。
5.4 合规红线:实操中的几个底线原则
最后必须说点大实话。DVWA这类靶场存在的意义,是让你在安全可控的环境里理解攻击原理。无论你在这套流程里玩得多熟练,都不能把同样的手段照搬到未经授权的真实网站上。网络安全的铁律永远是“先授权,后测试”。
我自己带人的时候反复强调三个底线:测试必须限定在靶场或授权范围内的资产,不碰任何生产环境和未授权系统;学习爆破原理是为了更好地做防护建设,而不是拿去搞破坏;任何实战项目的原始记录和报告,都要遵守所在机构的数据保密规范。守住这三条,安全学习才有持续性,把你手里的技能用在正道上,也是这个行业能长期健康发展的重要因素。
我个人在实际操作中的体会是:爆破这种攻击方式,从技术本身看确实不复杂,但它折射出来的安全隐患非常真实——弱口令、无防护的登录接口、粗糙的会话管理,这些才是被爆破得手背后的根本原因。所以我每次做完一轮DVWA爆破练习,都会顺手回去检查一下自己负责的系统有没有类似的短板,该加验证码的加验证码,该上锁定策略的上锁定策略。攻击视角和防御视角来回切换,才是这套练习的最大收获。
最后再分享一个小技巧:做完HIGH级别的爆破绕过之后,不要急着关闭项目,试着把同样的循环脚本改造成一个简单的“账号密码检测器”,输入一批用户名和一批密码,自动记录哪些组合能登录成功。这个脚本不仅能用在DVWA,稍加改动就能应用在很多有授权测试的内部系统弱口令自查里。把一次性的攻击练习沉淀成可复用的检测工具,这个项目才算真正发挥了它应有的价值。