ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

BWAPP靶场SQL注入通关实战:从联合查询到盲注绕过

BWAPP靶场SQL注入通关实战:从联合查询到盲注绕过 提起Web安全入门绕不开的就是SQL注入绕不开SQL注入练习自然就绕不开BWAPP这个靶场。我刚入行那会儿白天看了一堆SQL注入的原理文章晚上回去就想找个地方练手。DVWA的界面太老Pikachu的题目量少最后真正让我把SQL注入从“看着会”练到“上手能打”的还是BWAPP。这是一个把各种常见漏洞场景打包好的PHP练习环境里面光SQL注入模块就有近十个入口既有GET型、POST型也有JSON、登录表单和SQLite场景Level还分了Low、Medium、High三档。今天这篇不聊虚的就聊聊我在BWAPP上把SQL注入模块从Low刷到High的完整通关过程以及那些踩过之后才知道的坑给准备刷这个靶场的朋友一份能直接照着操作的流水账。1. 认识BWAPP靶场与SQL注入演练环境1.1 BWAPP到底是什么和其他靶场有什么不一样BWAPP全称是Buggy Web Application翻译过来就是“故意做得漏洞百出的Web应用”是一个开源的PHPMySQL靶机项目。和DVWA这类单页面应用不同BWAPP等于把几十种主流Web漏洞场景全部拆开做成了一套独立的模块化应用你登录进去之后会看到一个类似“漏洞目录”的列表点哪个就进入哪个漏洞场景每个场景都有独立的参数、逻辑和后台代码。对我这种刷题型选手来说BWAPP最大的优点就是“分类清楚、题型足够多”。拿SQL注入来举例DVWA基本就是User ID查询那一个页面最多就是SQL Injection和SQL Injection (Blind)两个入口而BWAPP单独把SQL注入列成了一个分类下面有GET搜索、POST搜索、JSON请求、登录表单、SQLite等多个入口每个入口的服务端写法都不一样。这意味着你在同一个靶场里能同时练习“在URL传参注入”“在POST表单注入”“在JSON数据结构里注入”这三种常见场景而不是换一个靶场就要重新熟悉一遍界面。另外还有一点我很喜欢BWAPP每个漏洞模块都提供了PHP源码查看入口。当你注入不进去、想不明白为什么过滤规则这么奇怪的时候点开源码看一行就知道服务端到底做了什么处理。这对新手来说极其友好因为学习漏洞本质上是学习“代码为什么会写成这样”而不是单纯背几个payload。1.2 为什么练习SQL注入优先推荐BWAPP我就直接给结论如果你是零基础或刚入门想通过刷靶场把SQL注入彻底吃透BWAPP的优先级应该排在DVWA和Pikachu前面。原因有三点。第一题目梯度设计合理。BWAPP每个模块分三个安全级别Low级别几乎没有任何过滤适合理解注入原理Medium级别加了一些过滤函数适合练习“绕过滤”High级别要么用了预处理查询要么做了严格的字符过滤不适合继续硬打反而适合用来理解“怎么修代码才能防注入”。这一条龙下来等于把攻击和防御都过了一遍。第二场景覆盖足够广。前面说了GET、POST、JSON、登录表单、SQLite都有。实际上做Web安全的都清楚同一个SQL注入漏洞换一个传参位置手感和坑都不一样。比如GET注入可以直接改URL但POST注入就要借助Burp Suite抓包改数据JSON注入如果格式化不当payload很容易被转义掉。这些场景差异只有在同一个靶场里连续对比才能印象深刻。第三它给了你“高级别打不动”的真实正反馈。很多靶场到高等级仍然用字符串拼接SQL只是加个过滤函数所以还能靠技巧绕但BWAPP的High级别直接改成预处理查询Prepared Statement这是根儿上的修复方案。我在刷到高级别时会碰壁这反而让我明白了一件事现实中遇到规范开发的系统SQL注入之所以难打不是因为你不会绕过而是因为人家压根没用拼接SQL。简单对比一下资源对比项BWAPPDVWAPikachu漏洞模块数量多按分类展示少固定几个页面中等适合专项练习SQL注入入口种类GET、POST、JSON、登录、SQLite主要是GET和CookiesGET、POST为主安全级别Low、Medium、HighLow、Medium、High、Impossible无等级概念按题目固定源码查看内置入口方便对照需到官方下载源码文件部分页面有提示难度曲线平缓适合入门比较陡Impossible很难偏基础题型套路固定2. 环境搭建与登录准备先把靶场跑起来2.1 本地部署的两种方式与版本坑BWAPP的搭建方式网上有好几种我推荐两种最省事的方法一种是直接用Docker另一种是用小皮面板或者phpstudy这类集成环境。用Docker是最省心的找到官方镜像或社区维护的镜像一条命令拉下来启动即可。但我更推荐本地装PHP集成环境因为BWAPP很多时候需要你查看源码、调php.ini配置装在本地方便折腾。我当时的搭建步骤大概是这样下载源码放进小皮面板的网站根目录启动Apache和MySQL浏览器访问http://127.0.0.1/bwapp/第一步会跳转到安装页面。安装页会让你选择数据库类型默认填MySQL数据库名填bwapp账号密码按本机MySQL的实际情况填之后点“Create Database”基本就完成了。这里有一个大坑很多新手卡在这里BWAPP的源码比较老对PHP 5.x版本兼容最好如果用PHP 7.x或更高版本运行安装页面能打开但进入某个漏洞模块时可能白屏或直接报错。实际上它主要的问题是老的mysql函数和PHP 7后废弃的语法。解决办法有两个方向一是把PHP版本切到5.6或7.2二是如果坚持用新版PHP遇到白屏模块就去改对应文件里的写法。我个人实测下来下载一个PHP 5.6版本的小皮面板跑BWAPP基本不会遇到兼容性问题。如果你就是想在新版PHP下强上那至少要开启php.ini里的display_errors不然报错被吞掉你连从哪儿下手都不知道。2.2 初始化数据库与安全级别选择安装完以后进入主界面之前你需要登录默认账号密码一般是bee和bug这是BWAPP自己的预制账号。登录界面上还有一个security level的选项这就是我刚才说的难度分级。我建议新人先把级别设为Low跑通一遍SQL注入流程后再切成Medium去练绕过。这里特别提醒一句每次切换安全级别后最好重新登录一次否则部分页面可能加载的还是上次级别对应的会话状态。这不是逻辑错误而是会话和级别绑定在小细节上的问题我第一次刷的时候没注意切到Medium后页面还是没过滤差点以为是靶场坏了折腾了半天才发现是会话没刷新。在正式进入模块之前建议先在浏览器里按F12打开开发者工具把Network面板开出来。后面很多注入操作需要你看请求参数、响应内容甚至手动改包。我之前就是懒得开开发者工具在浏览器地址栏里直接改URL参数结果遇到POST型注入的时候完全无从下手后来老老实实配好了Burp Suite才顺畅起来。2.3 定位SQL注入模块它有哪些入口登录后主页面会显示一个漏洞分类列表点进“SQL Injection”分类你会看到类似这样的模块列表SQL Injection (GET/Search)SQL Injection (POST/Search)SQL Injection (GET/JSON)SQL Injection (Login Form/Hero)SQL Injection (SQLite)SQL Injection (Blind)SQL Injection (Stored)这里我建议按顺序刷先从GET/Search开始因为它的请求参数直接暴露在URL里最适合理解注入流程。后面的POST/Search和JSON涉及请求体修改需要抓包工具配合Login Form是登录绕过场景考验的是万能密码逻辑Blind对应盲注主要练布尔盲注和时间盲注SQLite则是基于SQLite数据库的注入语法上略有差异Stored属于存储型注入虽然也是SQL注入但重点是写入和后台展示不是我们今天说的查询型注入主线。刷的顺序我个人的建议是GET/Search先打通搞定联合查询和报错注入然后去Login Form练万能密码再回头做POST/Search和JSON最后再碰Blind和SQLite。这样难度爬坡最平缓思路也能接得上。3. 联合查询注入从判断注入点到拖出整张表3.1 三步判断注入点报错、恒真、恒假进入SQL Injection (GET/Search)模块页面是一个搜索框输入一个电影名称比如iron man点击搜索后URL会变成类似sqli_1.php?titleironmanactionsearch。先观察正常请求和响应确认参数title把值传到了后端SQL查询语句中。那么怎么判断这里能不能注入呢我的固定打法分三步。第一步输入一个单引号观察页面是否报错。如果后端是直接把字符串拼进SQL语句那么title这种结构就会因为多出一个单引号造成语法错误页面要么直接报MySQL错误要么返回一个无记录的结果页。在BWAPP的Low级别下报错信息是直接展示在页面上的所以这一步非常明显。第二步输入恒真条件比如iron man and 11因为11永远成立所以查询结果应该和正常搜索一致。第三步输入恒假条件比如iron man and 12因为12永远不成立查询结果应该变成空白。如果两步的返回结果有明显差异那就说明单引号确实参与到了SQL语句的字符串闭合里存在字符型注入。这套“报错恒真恒假”的判断方法不只是针对BWAPP遇到任何SQL注入点都可以先套一遍。很多新手上来就union select结果注入点都还没确认自然什么都回显不出来。我见过不少朋友在真实授权测试里也是这个毛病判断注入点的耐心是没有的一条payload梭哈打不出来就喊着不存在漏洞。判断注入点其实花不了多少时间却是整个注入流程里最不能省的一步。3.2 order by确定字段数和union select定位回显点确认了注入点接下来要回答两个问题当前SQL查询查了几列页面回显了哪一列答案需要用到order by和union select。先确定字段数payload长这样iron man order by 1-- iron man order by 2-- iron man order by 3----是SQL注释符作用是把后面的内容全部注释掉这样我们注入的片段后面的原始SQL就不会干扰我们的语句。在URL里直接传这串字符时注意注释符后面的空格会被浏览器丢掉要么用%20代替空格要么换用#注释符。我一般习惯用Burp Suite的Repeater来发请求这样不会遇到浏览器自动编码的困扰。order by后面的数字代表按照第几列排序。如果数字大于实际查询列数就会报错“Unknown column”。所以在BWAPP的GET/Search场景里从1开始逐个递增当order by 7报错而order by 6正常时就说明查询语句取了6列。接下来用union select判断回显位iron man union select 1,2,3,4,5,6--union的作用是合并两次查询的结果前面查询不到数据时后一组数据就会显示出来。页面如果正常回显你会看到一排数字1到6但页面上通常只显示其中某几个数字。此时页面上出现数字的位置就是联合查询的可回显列。比如页面只显示3和4说明第3和第4列的位置可以输出我们想要的数据。记下这个位置后续获取数据库名、表名的时候只要把对应位置的数字替换成我们要读取的函数或表达式就行。3.3 利用information_schema一步步拿库、表、字段、数据拿到回显位以后剩下的就是套模板了。先爆数据库和版本信息iron man union select 1,database(),version(),user(),5,6--在BWAPP里执行完页面会显示当前数据库的名字一般默认是bwapp同时会显示MySQL版本和当前用户。看到这些信息说明整条链路已经打通了。接下来获取该库下所有表名iron man union select 1,group_concat(table_name),3,4,5,6 from information_schema.tables where table_schemadatabase()--information_schema是MySQL自带的“数据库的数据库”里面保存了整个实例的元数据tables表存放了所有库名和表名的对应关系。group_concat函数则可以把查询到的多行结果合并成一行便于在单个回显位输出。BWAPP里通常有一张users表看名字就知道是存放用户账号的表。继续查这张表的字段iron man union select 1,group_concat(column_name),3,4,5,6 from information_schema.columns where table_nameusers--这里注意table_name的值如果直接写users引号需要和前面的字符串闭合配合好。如果觉得引号总被过滤可以改成十六进制形式where table_name0x75736572730x开头后面的部分就是字符串users的十六进制表示。这个技巧在Medium级别过滤单引号的时候特别有用建议先记下来。查到users表里有login和password字段后直接查数据iron man union select 1,group_concat(login,0x3a,password),3,4,5,6 from users--0x3a是冒号:的十六进制写法用来把login和password在输出时拼接成admin:密码的格式方便一眼看明白对应关系。到这一步一次完整的联合查询注入就结束了。整个链路总结下来就四个词判断注入点、order by数列、union找回显、information_schema查元数据。这套流程适用于绝大多数MySQL环境下的查询型注入。4. 盲注与过滤绕过没有回显怎么继续打4.1 布尔盲注原理与手工二分法联合查询虽好但前提是页面会把查询结果展示出来。BWAPP的SQL Injection (Blind)模块就专门设计成了不显示数据内容只显示“存在记录”和“不存在记录”两种状态。这种情况下我们就要用盲注。布尔盲注的核心思路构造一个条件判断让SQL语句根据条件的真假返回不同的结果然后通过观察页面差异判断条件是否成立。比如iron man and ascii(substr(database(),1,1))97--这条语句的意思是先截取当前数据库名字的第一个字符转成ASCII码判断它是否大于97字母a的ASCII码。如果数据库名第一个字符的ASCII码大于97条件成立查询结果和正常无差异否则查询结果为空。那怎么从“大于97”变成具体的字符呢用二分法。比如判断到大写还是小写如果ASCII大于97说明第一个字符在字母a到z之间接着判断是否大于109也就是字母m如果大于109范围缩小到n到z再判断是否大于115……如此反复每次把范围缩小一半一个字符最多猜7次就能确定下来。手工盲注虽然慢但当你一步步把数据库名、表名、字段名全部猜出来的时候对SQL执行逻辑的理解会深刻非常多。我建议在BWAPP练习盲注时配合Burp Suite的Repeater做半自动测试因为页面返回的布尔差异要在响应长度上观察。Burp里右键“Send to Repeater”改payload看响应包的“Length”值有记录时是300多字节无记录时是200多字节差异一目了然。等盲注练熟了再考虑写脚本来自动化否则直接上脚本很容易变成“跑payload的机器”盲注的原理反而不清楚。如果觉得手注太慢也可以直接用Python写一个简单的布尔盲注脚本用requests库发请求通过响应内容判断页面状态先返回正常搜索时页面里的某个唯一字符串做基准再拼上注入条件逐字提取。脚本核心逻辑大概长这样import requests import string url http://127.0.0.1/bwapp/sqli_4.php cookies {PHPSESSID: 你自己的会话ID} charset string.printable def get_payload(pos): # 判断database()在第pos个字符的ASCII码 return firon man and ascii(substr(database(),{pos},1)){mid}-- # 对每一位字符做二分逼近这里省略了请求构造细节核心是多轮请求收敛范围写脚本的时候要注意设置延时不然请求太快本地MySQL跟得上但如果是模拟线上靶场容易被软性限频。而且脚本跑起来之前一定要先手动确认两次不同条件的响应确实有稳定的差异否则脚本等于在瞎猜。4.2 文本过滤后的绕过技巧把安全级别切换到Medium之后你再回GET/Search模块用之前的payload会发现自己输入的某些字符被过滤掉了。这时页面可能直接提示Hacking attempt或者把空格和注释符给替换成了空字符串。我切到Medium级别第一次被打蒙时第一反应是去翻源码。打开sqli_1.php一看果然有一段过滤函数它会过滤空格、and、or等关键字。这时就要用“等价替代”的思路来绕。空格被过滤可以用注释符/**/代替。SQL语句中/**/和空格在大多数MySQL场景下是等价的所以order by 3可以写成order/**/by/**/3。关键字被过滤可以用大小写变形MySQL默认对关键字大小写不敏感UNION、Union、uNiOn效果一样。如果select这种纯关键字被过滤还可以用内联注释/*!50000select*/这种MySQL特性写法不过实测下来BWAPP的Medium级别没到这么狠的程度大小写和注释符交替用就够了。常见过滤方式与绕过对应关系我整理成了表过滤内容绕过方法示例空格使用/**/代替order/**/by/**/3and/or用和||或大小写变形1 11单引号使用十六进制绕过table_name0x7573657273逗号使用join或from结构替代limit 1 offset 0注释符使用url编码或换行符--%20或#select内联注释或大小写/*!select*/这里要特别提醒切换Medium级别后之前已经登录的会话可能还残留如果发现过滤规则没生效先退出重新登录确认当前级别确实是Medium。BWAPP在这块的逻辑是页面加载时读取会话里的级别设置一旦判断条件读到的还是Low过滤代码就不会执行这是很多人刷题时容易忽略的干扰因素。4.3 万能密码与登录表单注入场景登录表单注入是BWAPP里非常经典的一个模块入口是SQL Injection (Login Form/Hero)。场景本身就是一个登录框要求输入账号密码。它的SQL语句大概长这样SELECT * FROM users WHERE login$user AND password$password如果后端直接用用户输入拼接SQL我们就可以在用户名框输入一个“万能密码”来绕过登录admin or 11--把它代入SQL语句中字符串闭合之后就变成了SELECT * FROM users WHERE loginadmin or 11-- AND passwordanything因为11恒成立loginadmin or 11这个条件整体为真注释符又把后半段密码判断全部注释掉了所以查询直接返回了第一条记录登录被成功绕过。这就是网上说的“SQL注入万能密码”的底层原理不是什么神奇的魔法就是字符串闭合和逻辑运算的组合。在BWAPP里刷这个模块时我建议同时打开源码看它Low、Medium、High三个级别分别怎么处理的。Low级别直接拼接漏洞百出Medium级别可能用了mysqli_real_escape_string单引号被转义万能密码就失效了High级别基本使用了预处理查询完全免疫。看懂这三层变化后你就理解了为什么现代的开发规范强调要用参数化查询而不是靠输入过滤来防注入。任何黑名单式的过滤都有可能被绕过只有参数化和预编译才是根治方案。5. 常见问题与排查技巧实录5.1 白屏、报错、乱码环境问题优先排刷BWAPP时遇到的最多问题不是注入不进去而是环境本身出问题。我遇到的第一个白屏是在进入某个POST注入模块时整页空白什么都没显示。当时第一反应是漏洞模块写坏了折腾半天才发现是因为PHP版本太高这个页面用到了一个老函数新版PHP已经把它移除了。后来我在php.ini里打开display_errors和error_reporting(E_ALL)刷新页面看到具体报错信息才知道问题出在哪。所以刷靶场之前先把PHP错误显示打开这一步能省掉很多无意义的排查时间。如果你用的是小皮面板的PHP 5.6默认配置一般没问题如果是PHP 7.x建议先把版本切回5.6或者找社区修复过的BWAPP版本。另一个常见问题是数据库初始化失败安装页面提示“Database doesnt exist”。这种情况多半是数据库账号权限不够或者数据库名填的不是bwapp。直接进MySQL命令行手动创建一个bwapp库再给当前用户赋上这个库的所有权限重新执行安装脚本就行。还有乱码问题页面中文或特殊字符显示成问号。这通常是数据库连接字符集和页面编码不一致造成的。BWAPP默认大多用latin1而浏览器用的是UTF-8在查看中文数据时就会乱码。你可以把PHP代码里的连接字符集改为utf8或者在MySQL配置文件里设置character_set_serverutf8改完重启MySQL再刷新页面就正常了。5.2 语句不生效编码、注释符、请求方式三连查注入payload明明看起来没问题但页面就是不回显或者直接返回500这事我在Medium级别刷过滤的时候经常碰见。后来总结了一套排查顺序按三步走基本能定位问题。第一步查URL编码。在浏览器地址栏直接输入和#等特殊字符时浏览器可能不把它们作为URL的一部分甚至直接丢弃。比如#在URL里是锚点的意思后面内容根本不会发送给服务器。所以我后来在浏览器里验证payload时尽量用Burp Suite的Repeater或者用curl命令行避免浏览器自作主张做编码转换。第二步查注释符。SQL注入里的--注释符必须带空格才能生效但如果是在URL里传末尾的空格又会变成%20有时后端会把%20解码成空格问题不大但有时浏览器或后端框架会把末尾空格去掉导致--变成--而--这个写法在MySQL里后面必须有空格否则不视为注释。遇到这种情况要么用#注释符要么用--%20显式编码空格。第三步查请求方式。GET注入可以改URLPOST注入就要改请求体。很多人把GET/Search里的payload原样搬到POST/Search里结果发现参数名都变了自然打不通。POST注入的排查思路是先看请求体里有哪些字段比如titlexxxactionsearch然后把注入点放在titlexxx的值里而不是放在URL里。如果一开始就用Burp Suite抓包看请求体这种低级错误完全能避免。5.3 从Low到High学会看懂源码修复逻辑刷完Low级别和Medium级别后如果还卡在High级别打不进去我的建议是别硬扛。打开模块附带的源码入口看一下High级别的处理代码。通常你会看到类似这样的逻辑先用mysqli_real_escape_string对用户输入做转义更高一级则直接改用预处理语句$stmt $link-prepare(SELECT * FROM movies WHERE title LIKE ?); $stmt-bind_param(s, $title); $stmt-execute();看到这一行你就该明白High级别已经不存在SQL注入了。这不是“过滤没绕过去”而是后端从根本上改变了SQL执行方式。参数占位符?会把用户输入当作纯数据处理而不是SQL代码的一部分任何注入payload都无法改变SQL语句本身的结构。所以刷到High级别打不动时正确的收获不是“我技不如人”而是“我知道了怎么修代码”。PWAPP这个靶场最有意思的地方就是它通过保护级别把攻击和防御两条线放在同一个场景里。当你把Low、Medium、High全部对照源码看完其实一套完整的漏洞分析报告就构成了漏洞成因、攻击路径、过滤失效分析、修复建议全齐了。我个人在刷完整个SQL注入模块后最大的感触是SQL注入难的不是背payload而是理解SQL语句是怎么被拼接出来的。只要你会看SQL的执行上下文知道引号和注释符到底闭合了什么就算把BWAPP的题目全忘记换到真实环境里也一样能顺着思路拿下来。靶场永远只是起点但它给了你一个怎么折腾都不会出事的安全环境请务必在授权和合法范围内练习。下一步你可以继续往后刷报错注入、堆叠注入和SQLite场景那些又是另一片新天地。
返回列表