前言
看完了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