thinkphp5.0.X反序列化利用链审计

前言

看完了thinkphp5.1.X反序列化利用链,现在来看thinkphp5.0.X反序列化利用链,难度的话要比上一个要高一些,这才好玩233,总呆在舒适区也没啥意思。

0x01

安装compose一把梭

composer create-project topthink/think=5.0.14 tp5.0.14

环境:
PHP-7.0.12-NTS + Apache

在5.1版本中我们pop链的出口是Request类的__call方法,可以直接调用,但是在5.0中
$hook[$method]处的写法不一样,在5.1.x中是$this->$hook[$method],这里对于我们来说是可控的,在5.0.x中的写法是self::$hook[$method]这里是const类型的,所以是不可控的。所以我们不能使用Rquest类作为一个出口了,那就需要找其他可利用的 __call 方法。
这里我们利用的
thinkphp/library/think/console/Output.php,Output类中的__call方法后续可以当跳板。

择Output类中的__call方法
POP链入口点依旧是
 thinkprocesspipes:__destruct 方法

通过上篇文章就已经学习到了
可以看到有一个unlink函数,如果我们能够控制$filename,就可以达到任意文件删除。
exp:

<?php
namespace thinkprocesspipes;
class Pipes{
}

class Windows extends Pipes
{
    private $files = [];

    public function __construct()
    {
        $this->files=['D:PHPSTUDY2018PHPTutorialWWW	p5shell.php'];
    }
}

echo base64_encode(serialize(new Windows()));

自然而然,我们开始寻找RCE点看file_exists这个函数。

这里就直接写回溯过程了:

__toString -> toJson -> toArrary

需要注意的是在5.0.x没有含有__toString方法的Conversion类,
这里我们选择 thinkModel 类来触发。由于该类为抽象类,所以我们后续在构造 EXP 的时候得使用其子类,例如: thinkModelPivot 类

在toArray方法中,我们要找到可以利用的类似$a->function($b)方法,而且$a,$b必须可控,能让我们执行__call。
和之前一样,分析一下具体实现toArray类的代码

Model抽象类的toArray方法,存在三个地方可以执行__call。
这里我们使用的_call方法已经确定,
thinkconsoleOutput:__call() 方法
我们需要通过第三个来触发__call() 方法

$item[$key] = $value ? $value->getAttr($attr) : null;

这里要求$value和$attr都是可控的
依次跟进去看。
先看value

关键代码

$modelRelation = $this->$relation();
$value         = $this->getRelationData($modelRelation);

我们先看$modelRelation这个变量怎么来的:

看$name:

$name可控:$this->append可控

$modelRelation也就是 thinkModel 类任意方法的返回结果
这里选择
Model->getError方法,因为它的返回值error是非常简单可控的

再来看getRelationData

看到传入的$modelRelation需要Relation类型(可以通过$this->error来控制这个类)

$this->parent 肯定是 thinkconsoleOutput 类对象,也就是$value->getAttr($attr)中的$value,这里要求是Relation类(这里可以通过$this->error来控制这个类)。
我们来看Relation类中的ifSelfRelation

isSelfRelation方法简单可控

getModel方法简单可控

ps:poc中就是把$this->query设成Output,直接跳到Output类

这里的get_class方法要求$modelRelation->getModel()和$this->parent为同类,也就是要求$value->getAttr($attr)中的$value和上面简单可控的model为同类,这样我们就控制了$value->getAttr($attr)中的$value。
下面看$attr

$attr值,由$bindAttr = $modelRelation->getBindAttr();控制

全局搜索getBindAttr方法
存在这个方法的Relation的子类为OnetoOne类,这里的binAttr简单可控

OnetoOne也是抽象类,进行全局搜索OnetoOne的子类,最后选定这里的Relation子类为HasOne
这里解释一下因为是抽象类,我们得找一个能具体实现它方法的子类,所以我们选择了HasOne
到此

我们已经能够执行$value->getAttr($attr)
当代码执行到$item[$key] = $value ? $value->getAttr($attr) : null;就能够执行Output类__call魔术方法,下面我们来看一下我们使用的魔术方法。

跟进block方法

!

这里$this->handle可控,全局搜索write方法

类: Memcached
搜索set方法

这里可以进行文件写入
怎么写呢?我们继续看细节
首先$filename由$this -> getCacheKey方法控制。
跟进

$filename可控

下面要看的就是$data了

注意这里:

调用set方法中的参数来自先前调用的write方法只能为true,且这里$expire只能为数值

这样就不能写shell。

继续回去看

有个setTagItem方法
跟进发现

会再执行一次set方法,且这里文件内容$value通过$name赋值(文件名)

$filename可控且可以利用伪协议绕过中间的exit
最后我们选择rot13和伪协议绕过这个东西

我们把$option['config']设置为

php://filter/write=string.rot13/resource=<?cuc @riny($_TRG[_]);?>

最后马的文件名为‘‘.md5(‘tag_’.md5($tag)).’.php’。

到此,整个POP链构造完成,
图片来源:
https://www.anquanke.com/post/id/196364

POC就不放了,看了一下还是很好找的
其实实战价值我觉得不是太高,一条鸡肋反序列化链,哪个程序员允许我们控制整个路径或者反序列化过程。。。
最后要是windows系统poc利用失败(Windows系统里面创建不了含有某些特殊字符的文件),换个Linux

主要还是用来锻炼审计能力
今天接了一个给公司出题的活,emm100道题。。赚点零花钱两星期内应该不会更博了233

参考

https://www.anquanke.com/post/id/196364

http://blog.ydspoplar.top/2020/01/31/thinkphp5.0.x-反序列化/

原文地址:https://www.cnblogs.com/wangtanzhi/p/12663572.html