thinkphp-序列化5.0.x

写在前面

(1)之前已经审计过了5.1.x的pop链,最后RCE的出口是Request类的__call方法。但是5.0.x和5.1.x在$hook[$method]处的写法不一样,在5.1.x中是$this->$hook[$method],这里对于我们来说是可控的,在5.0.x中的写法是self::$hook[$method]这里是const类型的,所以是不可控的。所以我们不能使用Rquest类作为一个出口了,要找其他的__call


(2)在5.0.x没有含有__toString方法的Conversion类,这里我选择用Model类,这里的操作都是一样的

__toString -> toJson -> toArrary

分析

toArray方法中,我们要找到可以利用的类似$a->function($b)方法,看到这里的$item[$key] = $value ? $value->getAttr($attr) : null;

这里要求$value$attr都是可控的,先看$value

控制$value的核心代码就是901,902两行,$relation$name控制的,也就是由$this-append控制的,这是我们可控的,这也就是说$modelRelationModel类任意方法的返回值,这里我们选择Model->getError方法,因为它的返回值error是非常简单可控的

再看getRelationData方法

这里的$this->parent就是我们要触发的代替Request类的类也就是$value->getAttr($attr)中的$value,这里要求是Relation类(这里可以通过$this->error来控制这个类)。我们来看Relation类中的ifSelfRelation方法和getModel方法。

isSelfRelation方法简单可控

    public function isSelfRelation()
   {
       return $this->selfRelation;
   }

Model->getModel

    public function getModel()
   {
       return $this->query->getModel();
   }

getModel方法返回的是$this->query的getModel方法,query应该是Query类,

Query->getModel

    public function getModel()
   {
       return $this->model;
   }

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

$attr$modelRelation->getBindAttr()控制,这里要求需要有getBindAttr方法,经过全局搜索,存在这个方法的Relation的子类为OnetoOne类,这里的binAttr简单可控,而OnetoOne也是抽象类,进行全局搜索OnetoOne的子类,最后选定这里的Relation子类为HasOne

到这里为止,我们已经能够执行$value->getAttr($attr)了,下面就是选择可用的__call方法,在这里我们选择Output类。

这里执行的是$this->block追溯一下。

所以最终执行的是$this->handler->write方法。

进行全局搜索什么可用的类可以调用write方法,这里我们把$this->handler设置为thinksessiondriverMemcached类。

在查看有没有可用的类可以调用set方法,我们把这里的$this->handler设为thinkcachedrvierFile

159行的$result = file_put_contents($filename, $data);可以进行文件写入。

$filename$this -> getCacheKey方法控制。

可以看到$filename可以由$this->options['path']控制,这里简单可控。下面要看的就是$data了,向上追溯,

File->set方法的$valueOutput->writeln中的newline参数控制,这里默认为true,所以我们就对$value不可控,也就是对$data即写入的内容不可控,再向下看,有setTagItem方法。

这里又会调用一次set方法,把$name当作$value写入新文件,也就是把$filename当作$data写入新的文件,因为这里的$data默认有exit,我们通过,rot13转换绕过这个东西。所以我们把$option['config']设置为php://filter/write=string.rot13/resource=<?cuc @riny($_TRG[_]);?>最后马的文件名为'<?cuc @riny($_TRG[_]);?>'.md5('tag_'.md5($tag)).'.php'

执行system('ls')成功

这里可能。。会有个问题就是权限问题无法写入, 改一下payload的文件路径即可,因为getCacheKey方法会mkdir一个755的文件夹。

这是完整的pop链



本文作者:星盟安全团队

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/123676.html

Tags:
评论  (0)
快来写下你的想法吧!

星盟安全团队

文章数:31 积分: 75

星盟安全团队---"VENI VIDI VICI"(我来,我见,我征服),我们的征途是星辰大海。从事各类安全研究,专注于知识分享。

安全问答社区

安全问答社区

脉搏官方公众号

脉搏公众号