FEATURED · 精选文章

BWAPP靶场SQL注入通关指南:原理、手工注入与绕过实战

发布时间 / 2026/9/16 19:31:26
来源 / 创域科博编辑部
栏目 / 资讯中心
BWAPP靶场SQL注入通关指南:原理、手工注入与绕过实战 很多刚开始接触Web安全的朋友第一个真正产生“我在挖洞”感觉的靶场不是DVWA就是BWAPP。我自己是先刷完DVWA的SQL注入关卡再转头打开BWAPP时明显感觉到这个靶场更贴近真实业务——它有登录框、有搜索框、有User-Agent头甚至还有XML数据传输注入点藏在各种你想不到的地方。今天这篇就专门聊BWAPP里的SQL注入通关从原理到实操把每一关怎么过、报错怎么排、绕过怎么写全部分享一遍。如果你正在刷靶场、准备面试或者只是想搞懂“SQL注入到底是怎么发生的”这篇文章适合你完整读一遍。我会按我自己的通关顺序来讲先讲BWAPP里SQL注入的关卡分布和难易梯度再逐个拆解GET注入、POST登录绕过、搜索型注入、盲注和过滤绕过最后把我在实战里踩过的坑整理成一份排查清单。内容尽量写得像我在旁边手把手带你打而不是教科书复读。1. BWAPP是什么为什么拿它练SQL注入1.1 靶场定位与适合人群BWAPP全称是Buggy Web Application直译过来就是“故意写满Bug的Web应用”。它的定位是给安全测试人员、开发人员和学生提供一个合法的、可重复练习的攻击环境覆盖OWASP Top 10里的大部分漏洞类型SQL注入只是其中一个模块。和DVWA相比BWAPP的题目更密集一个问题会拆出多个场景比如同一个SQL注入会分GET型、POST型、搜索型、盲注、XML型还会故意加各种过滤规则。这种“同一种漏洞换着花样考”的设计特别适合把注入基础打扎实。从难度上看BWAPP每一关都可以设置安全级别我主要用的是low和medium。low级别基本是纯裸奔的拼接SQL适合理解注入原理medium级别会加一些简单的过滤或转义适合练习绕过技巧。这个梯度安排和DVWA很像但BWAPP的关卡数量更多题型更杂刷完一遍再去做CTF里的SQL注入题比如ctfshow的web入门明显会觉得思路开阔了很多。1.2 通关前需要准备的环境我这边用的是PHP集成环境加BWAPP源码包几分钟就能搭好。如果你之前装过DVWA那路径很相似把下载好的BWAPP解压到Web根目录访问install.php初始化数据库它会自动建好库和表还会提示默认账号密码。整个安装过程不涉及任何复杂的配置比较省心。另外建议准备两个工具一个是Burp Suite用来抓包改包很多注入点在请求头里比如User-Agent、Referer光靠浏览器地址栏是不够的另一个是浏览器的开发者工具看请求参数、看页面源码用来观察注入点的回显位置。数据库端我喜欢用MySQL命令行配合查询方便直接验证SQL语句的正确性。后面讲到的所有注入语句我都默认是MySQL 5.x和MariaDB环境这也是BWAPP默认的数据库。注意所有注入测试请务必在本地靶场或获得明确授权的环境里操作别拿这些语句去测别人的网站。安全测试的基础是合规能力再强也要守住这条线。2. 先把原理讲透SQL注入到底是怎么发生的2.1 注入的根因查询语句被拼进去了SQL注入说穿了就一句话——程序把用户输入的内容没有经过任何处理直接拼接进了SQL查询语句。比如BWAPP里的SQL InjectionGET这关后端代码大概是这样的$sql SELECT * FROM movies WHERE id . $_GET[id];你访问id1的时候查询是正常的SELECT * FROM movies WHERE id 1但如果id的值是1 or 11拼接出来的语句就变成了SELECT * FROM movies WHERE id 1 or 11此时前面的id1虽然会报错但后面的or 11是永真条件整个查询就会把movies表里所有记录都返回出来。这就是万能密码和万能参数的核心原理。我在给刚入门的朋友讲的时候经常用“往快递单里塞私货”来打比方系统本来要打印一张收件人信息结果你在收件人一栏里填了“张三 AND 给所有人发短信”快递系统一看这串指令就真的照做了。SQL注入不是SQL本身的漏洞是整个“把外部输入当成代码执行”的流程出了问题。2.2 按参数位置与返回结果给注入分类BWAPP里的SQL注入关卡很多如果不分类很容易打着打着就乱了。我习惯按两个维度分类注入点在哪以及服务器回不回应你的查询结果。按注入点分GET型参数写在URL里比如?id1适合用浏览器直接改。POST型参数在请求体里比如登录表单的username、password需要抓包或者用开发者工具修改。搜索型参数会被拼进LIKE语句中比如WHERE title LIKE %$search%闭合引号和通配符要格外小心。Header型注入点藏在User-Agent、Referer等HTTP头里无法直接看到参数必须靠Burp Suite抓包修改。其他特殊类型比如XML注入、JSON注入参数以结构化数据格式传递BWAPP也有对应关卡。按返回结果分回显注入查询结果会直接显示在页面上你可以在页面里看到数据字段的位置。这种最轻松用union select就可以控制显示内容。盲注页面不会直接展示查询结果只能靠页面返回“正常”还是“异常”或者响应时间的快慢来判断。BWAPP里的Blind关卡就是典型的例子需要花更多耐心逐字符猜数据。理解了这两种分类后面的实操逻辑就很清晰了先探测注入点属于哪一类再选择对应的攻击手法。BWAPP的设计恰好把这些类型都覆盖了一遍所以我强烈建议按我下面的顺序从第一关开始打不要跳关。3. BWAPP手工注入过关实录3.1 GET注入关卡从探测到拿数据完整走一遍最经典的起点是页面左侧菜单里的SQL InjectionGET/Search。我先演示GET型那一关因为它的回显最直观适合建立完整的注入流程。第一步探测有没有注入点。访问类似下面的地址http://127.0.0.1/bwapp/sqli_1.php?titleIronactionsearch页面正常显示搜索结果。接着在title参数后面加一个单引号http://127.0.0.1/bwapp/sqli_1.php?titleIronactionsearch如果页面出现数据库报错比如You have an error in your SQL syntax说明单引号被拼进了SQL语句这里十有八九有注入。如果页面没有报错也不代表一定安全可能是开发者做了转义也可能是盲注场景后面再讲。第二步确定字段数量。用order by逐个尝试http://127.0.0.1/bwapp/sqli_1.php?titleIron order by 1-- -actionsearch这里的-- -是MySQL的注释符号用来把后面可能残留的SQL语句全部注释掉。逐个尝试order by 1、order by 2……直到页面开始报错。比如说order by 7正常、order by 8报错那就说明原查询有7列。这一步非常关键因为后面union select的字段数量必须和原查询一致否则怎么试都出不来。第三步找回显位。把id参数或title参数改成不存在的值再用union select占位http://127.0.0.1/bwapp/sqli_1.php?title union select 1,2,3,4,5,6,7-- -actionsearch页面上会显示数字2、3、5之类的回显位这些位置就是我们可以控制输出的地方后面拿数据库名、表名、字段名都往这里放。第四步拿当前数据库名和版本http://127.0.0.1/bwapp/sqli_1.php?title union select 1,database(),version(),4,5,6,7-- -actionsearch看到页面上显示数据库名和版本号之后注入点就是你的“内部人员身份”了。后续用information_schema继续取表名和字段名我放在第4.3节专门讲。实操心得GET注入最重要的一个习惯是“每次修改URL之前先想清楚当前语句拼出来是什么样”。我见过太多新手在union select这里卡半天原因是忘了注释符后面要加空格或者忘记把前面的参数改成不存在的值导致页面只显示第一行原始数据看不到自己的注入结果。3.2 POST登录绕过与万能密码不用密码也能进后台BWAPP的Login页面是练习POST注入的好地方。这里需要打开Burp Suite拦截登录请求看到请求体里的username和password参数。用一个经典万能密码试试username: admin or 11 -- - password: 随便填后端的SQL如果长这样$sql SELECT * FROM users WHERE login . $_POST[username] . AND password . $_POST[password] . ;拼接后就是SELECT * FROM users WHERE loginadmin or 11 -- - AND passwordxxx-- -把后面校验密码的部分全部注释掉了前面的条件是loginadmin or 11恒为真系统就认为你已经是admin用户了。利用这个思路很多登录框都能用一个简单的恒真条件直接绕过。这也解释了为什么程序里绝不能明文拼接SQL哪怕是一个简单的登录查询都可能被人用两个引号和一句注释打得妈都不认识。如果这关在medium安全级别下单引号会被转义成\万能密码直接失效。这时候可以观察报文格式部分情况下还能用宽字节注入绕过但更常见的思路是如果登录框不好打就转攻同一个页面的其他参数或者换一个注入点。BWAPP这种多关卡设计本身就告诉我们真实的Web应用不可能只有一个入口上一个入口堵死了总会有下一个入口没堵。3.3 搜索型注入与LIKE通配符的坑搜索框是另一个高频注入点。BWAPP的SQL InjectionSearch这关后端语句大概是$sql SELECT * FROM movies WHERE title LIKE % . $_GET[title] . %;普通人看到的是搜索框但SQL看到的是LIKE %xxx%。这里注入最大的坑是你要先把自己从两个%中“闭合”出来才能写完整的注入语句。推荐的payload是% union select 1,2,3,4,5,6,7-- -别看这个payload长得奇怪它其实做了三件事前导的%匹配了LIKE语句中后面的%中间的单引号闭合了字符串的字面量然后才开始写union语句最后的注释符把末尾残留的%注释掉。任何一个部分少了语句都闭合不上。我做这道题时最开始直接套用GET注入的payload结果页面死活不显示数据后来才明白是忘记考虑百分号。这个坑在真实漏洞里也很常见凡是输入框被拼进LIKE查询的注入点闭合方式都和普通where条件不一样。所以看到搜索框不要下意识用开头的payload先想想前后有没有通配符。3.4 盲注场景没有回显怎么拿数据BWAPP菜单里有一关叫Blind SQL Injection页面只知道查询是否成功不会把数据库内容显示出来。这关的逻辑是提交一个ID页面会告诉你“存在这个用户”或者“不存在”。这就构成了布尔盲注的条件。布尔盲注的核心是用条件判断逐步猜测数据一次只猜一个字符。比如判断当前数据库名的第一个字符是不是ahttp://127.0.0.1/bwapp/blind_sqli_1.php?id1 AND SUBSTRING(database(),1,1)a如果页面返回“存在”说明当前库名的第一个字符就是a如果返回“不存在”就换下一个字符。整个过程比较慢但原理非常清晰。如果布尔盲注页面根本没有差异可以考虑时间盲注。比如判断条件成立时让数据库睡眠3秒http://127.0.0.1/bwapp/blind_sqli_1.php?id1 AND IF(SUBSTRING(database(),1,1)b,SLEEP(3),0)用计时器观察页面响应时间3秒左右就是条件成立否则就是条件不成立。时间盲注比布尔盲注更稳但也更慢我通常在布尔页面没有区分度时才切换到时间盲注。在真实环境的排查中时间盲注还能用于绕过一些逻辑上“无回显”的查询场景比如INSERT、UPDATE语句里如果带着SLEEP函数可以根据耗时确认注入点是否有效。想提高手工盲注效率我建议学一下二分法不要从a开始一个个猜而是先判断字符的ASCII码是大还是小比如ASCII(SUBSTRING(database(),1,1))100然后逐步缩小范围每个字符从最多26次尝试降到8次左右。手工刷题练的就是这种判断思路等熟练之后再用工具脚本批量跑你会发现自己对盲注的理解完全不一样。4. 进阶关卡过滤、编码与绕过技巧4.1 字符过滤后的手工注入测试BWAPP的medium级别会模拟开发者的简单过滤比如把常见的关键字替换成空字符串或者对单引号做转义。这一节对应很多人查过的“sql过滤字符后手工注入漏洞测试”也希望讲清楚绕过过滤器时到底该想什么。先做一个简单的测试场景后端把select、union、and这些词直接替换成空字符串。这个时候一个直接的union select是打不进去的因为语句拼出来之后关键字已经没了。常见的绕过思路是双写比如ununionion selselectect如果后端只做了一次空替换union中的union被删掉之后剩下的还是union最终拼接出来的语句反而变成了合法的union select。这种手法有点“以子之矛攻子之盾”的意思在CTF题目里特别常见。另外还可以用注释符代替空格。很多过滤规则会把空格过滤掉但SQL解析器支持用/**/代替空格比如1/**/union/**/select/**/1,2,3-- -如果union和select没被过滤把空格换成注释符号往往能绕过只针对空格的过滤。除此之外MySQL还支持反引号包裹字段名、大小写混写等方式。具体的过滤规则决定具体的绕过方式没有一招通吃的万能payload所以关键是先摸清对方到底过滤了什么。4.2 常见过滤规则与绕过手段整理我把BWAPP和实际测试中常见的过滤规则整理成一个速查表方便你刷题时对照排查过滤内容表现常用绕过思路过滤空格空格被替换为空使用/**/、%09、%0a等空白字符过滤关键字select、union等被替换为空双写关键字如ununionion、selselectect过滤单引号单引号被转义或删除尝试宽字节注入、改用整型参数、用十六进制编码表示字符串过滤等号被替换为空用like替代比如where name like admin过滤注释符--或#被过滤尝试用/*结尾、利用闭合引号或者%00截断过滤逗号,被替换为空用join替代部分union场景或者用from重排表格里最后一项容易被忽略有些过滤会删除逗号导致substr(database(),1,1)这种函数没法用。此时可以用substr(database() from 1 for 1)这种写法替代因为MySQL的substr支持FROM ... FOR ...语法这个语法不需要逗号。类似的知识点需要平时多积累到现场才不会慌。需要提醒的是绕过过滤器时一定要“先验证再继续”。每绕过一步就用一个简单的布尔条件测试比如先确认and 11能正常显示再确认and 12能正常报错或隐藏最后才开始跑数据。如果跳过验证直接上复杂payload很容易在语句嵌套很深时迷失方向。4.3 从SQL注入到获取所有数据库名的完整语句很多人搜过“mysql数据库如何通过sql注入获取所有的数据库名”这里我单独写一下。拿到了union回显位之后获取所有数据库名的语句非常固定union select 1,group_concat(schema_name),3,4,5,6,7 from information_schema.schemata-- -information_schema是MySQL自带的“数据库的数据库”存放所有元数据。schemata表里的schema_name字段就是所有数据库的名字。用group_concat把多行结果拼成一行主要是为了方便在有限的页面回显位置里一次性看完。把这条语句放到前面GET注入关卡的title参数里实际URL就是http://127.0.0.1/bwapp/sqli_1.php?title union select 1,group_concat(schema_name),3,4,5,6,7 from information_schema.schemata-- -actionsearch页面上会显示一串库名比如bwapp、information_schema、mysql、performance_schema。接下来如果想知道某个库里有哪几张表就把范围缩小到目标库语句变成union select 1,group_concat(table_name),3,4,5,6,7 from information_schema.tables where table_schemabwapp-- -想查某张表里的字段就换成columns表union select 1,group_concat(column_name),3,4,5,6,7 from information_schema.columns where table_schemabwapp and table_nameusers-- -最后再回填到原始的查询中就能把users表的账号密码字段全部读出来。整个过程就是一条链库名 - 表名 - 字段名 - 数据。熟练之后你会发现只要回显位控制得住所有元数据信息都能通过information_schema查出来。这也是为什么我把“获取所有数据库名”单独拎出来讲因为它是整个复制数据库流程里的第一步也是最重要的一步。5. 通关过程中踩过的坑常见问题与排查技巧5.1 经典报错与解决思路刷BWAPP时我遇到过不少报错有些问题第一次看完全没有头绪后来复盘才发现答案特别简单。这里挑几个典型的列出来。先说“order by 一直不报错”。这种情况通常不是语句写错了而是数据太多每列都有值所以报错被淹没了。建议先把查询条件收窄比如title让结果集为空再用order by测试报错会更明显。再说“union select回显不出来”。排查顺序一般是字段数对不对、注释符后面有没有空格、前面的参数值是不是不存在的随机字符串。我见过最离谱的一次是因为走了代理浏览器自动把单引号转成了%27后端解出来又正常了但Burp里看起来却带编码导致我一直以为语句被截断了。后来统一在Burp的Repeater里发包才彻底避开浏览器的干扰。还有“页面中文乱码”的问题。这本身不影响注入判断但看着很干扰思路。我通常在浏览器端把编码切到UTF-8或者用Burp的Response渲染功能直接看渲染后的页面。掌握这些基本功实际刷题时会舒服很多。5.2 手工注入与工具结合的取舍很多新手刷题到一半就想着用sqlmap一把梭。我的看法是工具能用但不能在没理解原理的时候用。BWAPP这种靶场价值就在于训练手工思路如果每一关都直接丢给sqlmap你对SQL语句拼接、闭合方式、回显位置的敏感度很难建立起来。等手工刷完一遍再回头用sqlmap做验证或者处理一些重复性高的盲注会非常顺。我在实际项目中通常是“手工定思路、工具提效率”先用浏览器和Burp确认注入点和闭合方式再用sqlmap跑数据工具跑出来之后还要回到手工语句里验证一遍确保数据真实可靠。这种配合方式的前提是工具操作者知道自己在干什么而不是只会运行命令。5.3 SQL注入的防御基线练完了攻击最后说两句防御。SQL注入的根源是“将外部输入拼接进SQL语句”所以最有效的防御就是切断拼接这条路。现代开发中推荐的做法是用预编译参数化查询也就是先让数据库“知道”这是一条带参数的模板语句再传入用户输入值数据库会把输入当作纯数据而不是SQL代码的一部分。以PHP的PDO为例$stmt $pdo-prepare(SELECT * FROM users WHERE login ? AND password ?); $stmt-execute([$username, $password]);只要用了这种方式就算传入admin or 11它也只是被当成一个奇怪的字符串去匹配无法改变查询结构。除此之外对输入做白名单校验、数据库账号用最小权限、Web应用防火墙做兜底都是纵深防御的一部分。不过我一直强调过滤和校验是辅助参数化查询才是根本。想靠过滤黑名单防注入的人迟早会在某种编码或双写手法面前翻车。6. 结尾还想再多说几句BWAPP的SQL注入关卡我刷了好几遍每次重新打都有新发现。第一次是照着别人的payload抄第二次是自己推语句闭合第三次开始尝试在medium级别下手工绕过到后来再去打Pikachu和ctfshow的SQL注入题明显感觉很多套路是相通的。所谓“通关”并不是把所有关卡跑一遍就完事而是真正理解每一关背后那个“为什么”。你在BWAPP里看到的 or 11到真实漏洞里可能换成时间盲注但底层都是同一个拼接缺陷换的只是闭合姿势。最后分享一个个人习惯每次打完一个注入点我会把完整的URL和SQL语句抄在本地笔记里旁边再写一行“为什么这个payload有效”的注释。时间久了这本笔记就成了我自己的SQL注入速查手册。如果你也想在Web安全这条路上走远一点建议也养成这种记录习惯这比存一堆别人的payload库有用得多。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻