拓冰建站拓冰建站
首页 / 资讯中心 / 正文

BWAPP靶场SQL注入实战指南:从原理到通关

如果你刚看完SQL注入的基础理论正想找靶场把“判断注入点、联合注入、信息获取”这套完整流程亲手走一遍BWAPP是我目前用过最顺手的练手环境。相比DVWA那种极简模块BWAPP把SQL注入拆成了搜索框注入、登录表单注入、盲注等多个独立场景每一个都能单独重置难度很适合从零开始一条一条过。这篇文章我会按实际通关顺序从环境搭建讲到最后一个防御视角把我在BWAPP里做SQL注入题的完整思路、SQL语句、踩坑记录都写出来。无论你是刚准备打靶场的新手还是已经做过几道题但老在细节上卡壳这篇都能给你一份可以照着走的通关路径。1. BWAPP靶场与SQL注入为什么我推荐用它练手1.1 靶场搭建与基本配置BWAPP全称是Buggy Web Application是一个故意写满漏洞的PHPMySQL应用。它最方便的地方是自带一键安装脚本不需要手动建库。我是用PHP Study在本地搭的环境下载BWAPP源码包解压到Web根目录浏览器访问http://localhost/bwapp/它会自动跳转到安装页面点一下安装按钮数据库和默认配置就全部初始化好了。默认登录账号是bee密码是bug。进去之后能看到一个类似课程菜单的界面左侧按OWASP Top 10分类SQL注入相关题目集中在“OWASP Top 10”的A1分类下也可以直接在搜索框搜“SQL Injection”。我建议把PHP的display_errors打开也就是在php.ini里把display_errors On。这能让SQL报错信息直接显示在页面上对初学SQL注入的人来说报错信息就是最好的导航。1.2 SQL注入题目分布梳理BWAPP里和SQL注入相关的题至少有这些题目名称场景类型难度特点SQL Injection (Search)搜索框经典字符串拼接适合入门SQL Injection (GET/Post)参数传递练习GET和POST两种方式SQL Injection (Login Form)登录表单不需要拿数据绕过去就行SQL Injection (Login Form/Hero)登录表单变种闭合方式变化SQL Injection - Stored (Blog)存储型数据先入库再触发SQL Injection - Blind盲注无回显靠页面真假判断这个安排其实很科学。Search题型适合讲通用流程Login Form题型适合讲认证绕过Blind题型适合讲布尔盲注和时间盲注。我个人建议按这个顺序刷先搞懂有回显的联合注入再碰盲注。2. 先吃透原理一条SQL语句是怎么被“带偏”的2.1 从服务端代码看注入点的形成BWAPP的Search题目对应的后端代码很长这样$sql SELECT * FROM movies WHERE title LIKE % . $search . %;注意这里的$search是从用户输入直接拼接进SQL语句的。假设我在搜索框输入iron man那执行的语句是SELECT * FROM movies WHERE title LIKE %iron man%看起来没问题是吧但如果我输入的不是正常标题而是 or 11 --拼接之后就变成了SELECT * FROM movies WHERE title LIKE % or 11 -- %这条语句的语义就完全变了。前面LIKE %匹配所有行or 11让整个条件恒为真后面的--是SQL注释符把原本的%吞掉。于是这条SQL会返回movies表里的全部数据。这就是SQL注入的本质用户输入被当作SQL代码执行了。服务端以为$search只是一段普通字符串但实际上它携带了能够改变语句结构的语法符号。2.2 闭合前引号理解注入的核心思维我见过很多新手卡在第一步——输入然后看到报错不知道下一步该怎么办。其实报错就是在告诉你闭合方式。当你在BWAPP Search里输入一个单引号并提交页面会报MySQL语法错误错误信息里会还原出完整的SQL语句。看到那条语句你就知道当前查询用单引号包裹输入值。我们需要在注入语句里先闭合左边的单引号。再用注释符杀掉右边的单引号。把这个思路用大白话讲程序本来想把你关在SQL字符串的“引号牢房”里你输入一个就是撬开牢门再输入注释符--就是把身后的狱警嘴堵上之后你想在SQL语句里干什么都行。理解了闭合后面不管是遇到双引号包裹、单引号加括号包裹、还是数字型注入点思路都是同一个把输入从“值”变成“代码”。3. 搜索型SQL注入完整通关实录五步拿到全部数据3.1 第一步探测注入点与闭合方式在BWAPP的“SQL Injection (Search)”里我用iron man做正常搜索能搜到电影列表。然后我在搜索框输入页面直接报错SQL语句被还原出来。我确认了这里存在字符型注入且输入值被单引号包裹。接着我用经典判断语句验证iron man and 11 --页面正常返回。再试iron man and 12 --页面返回空。这组对比的意义在于and 11是恒真条件不影响原查询结果and 12是恒假条件导致结果集为空。如果两种输入返回结果不一样就说明我们的输入确实参与到了SQL逻辑中注入点实锤。3.2 第二步order by判断字段数拿到了注入点下一步是搞清楚SELECT语句查了几个字段。这一步决定了联合查询能不能用。我用的是order by递推法iron man order by 1 -- iron man order by 2 -- iron man order by 3 --在BWAPP的Search题里order by 3页面正常order by 4页面报错说明这条SQL只查询了3个字段。为什么order by能判断字段数因为ORDER BY后面跟的是列的序号如果序号超过查询列的数量数据库直接报“Unknown column”错误。用二分法递推很快就能定位边界。这里不需要注入进去真的把数据查出来只要看页面报不报错就够了。3.3 第三步union select找显示位知道有3个字段后我用union select构造联合查询iron man union select 1,2,3 --页面返回的结果里出现了数字“2”和“3”。这说明查询结果的第2列和第3列会被渲染到页面上第1列可能被程序拿来做了逻辑判断但没有直接显示。显示位非常重要因为联合注入的本质是“让我的注入语句的查询结果与原查询结果一起回显到页面上”。如果显示位只有2和3那我后续的注入语句就放在第2列或第3列。为什么union前后字段数必须一致因为UNION操作要求两个结果集拥有相同的列数否则MySQL直接报错。这就是为什么第三步必须先做第二步这两步是锁死的依赖关系。3.4 第四步读库名、表名、字段名拿到显示位接下来就是按“库名→表名→字段名→数据”的顺序逐层获取。首先拿库名iron man union select 1,database(),user() --页面显示database()的返回结果是bwappuser()是rootlocalhost。数据库账号是root意味着后续操作有很高的权限。接着查表名。我需要用到MySQL的元数据库information_schemairon man union select 1,group_concat(table_name),3 from information_schema.tables where table_schemadatabase() --这里用group_concat()把多个表名拼成一行方便页面回显。BWAPP的数据库里有movies、users、blog等表。我一眼锁定了users表。再查users表下的字段iron man union select 1,group_concat(column_name),3 from information_schema.columns where table_nameusers --返回的字段里有关键的login和password。information_schema是MySQL自带的系统数据库存了所有数据库、表、字段的元数据联合注入查它属于标准操作。3.5 第五步提取核心数据最后一步直接查users表的内容iron man union select 1,group_concat(login),group_concat(password) from users --页面把所有用户名和密码哈希全列了出来。到这里整条“搜索框→数据库数据”的链路就完全打通了。整个过程走完之后我回头看其实SQL注入就是一套固定的信息获取流程找注入点、数字段、找显示位、查系统库、拿数据。每一步都有明确的目的不是瞎试。这套流程在BWAPP里跑通之后换到DVWA的SQL注入模块、pikachu的SQL注入模块甚至CTF的入门SQL注入题底层套路都是一样的区别只是过滤规则和闭合方式不同。4. 登录表单的万能密码绕过与不同级别过滤差异4.1 万能密码的原理与实战BWAPP的“SQL Injection (Login Form)”是一道很有意思的题。页面只有一个用户名框和一个密码框没有搜索列表你不知道后台SQL长什么样但逻辑其实是最简单的。我输入用户名 or 11 --密码随便填一个直接登录成功。后台拼接出来的SQL逻辑是SELECT * FROM users WHERE login or 11 -- AND passwordxxx关键在于数据库先执行login or 11这一整段条件恒为真所以MySQL不再关心密码对不对直接返回第一条用户记录。程序拿到记录后判断“查询结果不为空”就认为是合法用户于是放行。这类题目在网上常被叫“万能密码”但我更愿意叫它“逻辑绕过”。它利用的是程序判断逻辑和SQL语义之间的错位程序以为只要查到了记录就是用户本人但注入语法可以让查询结果在你不知道任何账号密码的情况下不为空。4.2 medium和high难度的防护差异BWAPP的每道题右上角都可以切换low、medium、high三个难度。这个设计特别适合观察防护手段的进化。难度防御方式注入难度变化low无任何过滤直接注入medium对输入做addslashes转义单引号被加反斜杠需找其他闭合方式high更严格的输入过滤过滤更多特殊字符可尝试双写或编码medium难度下单引号会被转义成\直接注入会破坏语法。这个阶段需要用宽字节注入或者寻找代码里没有过滤的输入点来绕过。在MySQL的GBK编码下前面加%df可以吃掉转义用的反斜杠这是我第一次感受到“过滤不等于安全”这句话的分量。high难度下过滤规则更强题目也更接近真实的防护状态。BWAPP的好处是它把同一个漏洞做成三个难度让我能直观看到同一行代码在不同防护下的表现差异这对以后看真实代码的防御能力很有帮助。5. 通关路上的坑注释符、空格过滤与盲注判断5.1 注释符的选择-- 与 # 的细节差异我在初学SQL注入时困扰最久的就是注释符。为什么有时用--有效有时用#有效有时用--MySQL里有两种注释写法#单行注释直接写到行尾。--注意--后面必须跟一个空格或控制字符才算注释否则会被当成普通文本。在URL里传入空格会被编码成%20或所以GPC注入时我经常写--这里的在URL解码后是空格正好补足注释符的空格要求。我自己的经验是在GET参数里用--最省事在POST表单里用#最稳。因为POST不像GET那样有URL编码的额外步骤直接提交#就是注释符本身。5.2 发现空格被过滤时的替代方案有些题目的过滤规则会拦截空格。遇到这种情况我先用/**/替代空格。MySQL支持用/**/作为分隔符or 11可以改写成or/**/11。后来我在其他靶场里还遇到过只过滤/**/而不过滤%0a换行符的情况所以替代方案可以灵活组合。BWAPP的high难度在某些题里就设置了这类过滤做的时候多试几种编码方式。只要理解了一个核心过滤规则永远在跟解释器打仗。数据库解释器能认出空格的位置编写过滤规则的人不可能把所有可能的空白形式都列全。所以用/**/、%0a、Tab符等方式绕过是SQL注入里永远不过时的技巧。5.3 无回显场景的判断方法如果页面不回显任何数据也不能用union联合查询那就是盲注场景。BWAPP的“SQL Injection - Blind”就是这种情况页面只告诉你“存在”或“不存在”而且两种响应看起来差别很小。我的做法是布尔盲注。先构造一个恒真条件和恒假条件对比iron man and 11 -- iron man and 12 --如果两者页面不同说明我们可以通过真假条件逐位猜解数据。比如猜当前数据库名的第一个字符iron man and ascii(substr(database(),1,1))98 --如果页面正常说明第一个字符的ASCII码是98也就是字母b。这样一位一位猜效率很低但思路很清晰。有时间盲注场景下页面真假没区别就要用sleep()函数制造延迟来判断条件是否成立。BWAPP的Blind题目也可以切换难度high难度下会增加时间延迟的判断适合进阶练手。6. 打通靶场之后用防御视角重新审视SQL注入6.1 参数化查询为什么是根治方案把BWAPP的SQL注入题全部通关之后我最大的收获不是会了一堆注入技巧而是真正理解了“为什么要用参数化查询”。前面看到的所有注入点都有一个共同特征SQL语句是字符串拼接出来的。用户输入被当成代码的一部分交给了数据库。而PDO预处理的方式是这样的$stmt $pdo-prepare(SELECT * FROM movies WHERE title LIKE ?); $stmt-execute([% . $search . %]);prepare先把SQL语句的结构固定下来用户输入通过execute的参数传进去。数据库把参数当作纯数据而不是SQL代码来解析。哪怕我输入 or 11 --它也只是一段被查找的文本不可能改变语句结构。这就是为什么在所有防御手段里参数化查询是第一优先级。它能从语法层面彻底消灭SQL注入的可能性而不是像过滤黑名单那样和攻击者打游击战。6.2 找出自己的盲区输入验证与最小权限做完靶场题之后我回看自己之前写的代码发现有一个隐藏盲区我确实用了参数化查询但在排序功能里还是直接把字段名拼进了SQL。这种地方如果字段名来自用户输入同样会构成注入。所以防御不是一句“用了PDO就安全”就能概括的。排序字段、表名、列名这些无法用占位符的位置必须做严格的白名单校验比如用数组限定允许的值$allowedOrder [id, title, year]; if (!in_array($orderBy, $allowedOrder)) { $orderBy id; }另外还要遵循最小权限原则数据库账号不必要用root只给应用所需的查询权限。我在BWAPP里能用root直接读information_schema但如果应用账号只有SELECT权限即便注入成功能拿到的信息也有限。6.3 实战中的个人体会BWAPP的SQL注入系列全部走完前后花了我大概一周的零散时间。我最大的体会不是“SQL注入就那几招”而是这套东西的现实意义只要代码里出现字符串拼接的SQL无论外面套了几层WAF攻击者总有办法试出可用的绕过路径。反过来如果你在写代码的时候下意识地避免拼接SQL用参数化查询把数据和代码分开绝大多数SQL注入问题根本不会出现。靶场通关只是起点真正的成长是把攻击者的思路内化成做防御时的直觉。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门