thinkphp5.0.x-反序列化

这条pop链是thinkphp5.0.x从下午开始自己审的,差不多晚上的时候整完了,写完了EXP,不得不说反序列化文化真的博大精深。

写在前面

(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类,这里的操作都是一样的

1
__toString -> toJson -> toArrary

分析

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

image-20200228131320506

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

image-20200228131342919

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

image-20200228131447636

再看getRelationData方法

image-20200228131455389

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

isSelfRelation方法简单可控

1
2
3
4
public function isSelfRelation()
{
return $this->selfRelation;
}

Model->getModel

1
2
3
4
public function getModel()
{
return $this->query->getModel();
}

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

Query->getModel

1
2
3
4
public function getModel()
{
return $this->model;
}

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

image-20200228131505577

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

image-20200228131515081

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

image-20200228131520786

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

image-20200228131526505

image-20200228131532494

image-20200228131537890

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

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

image-20200228131544909

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

image-20200228131549871

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

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

image-20200228131556096

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

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

image-20200228131601357

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

![image-20200228131607133](/Users/largepoplar/Library/Application Support/typora-user-images/image-20200228131607133.png)

执行system('ls')成功

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

这是完整的pop链

image-20200228131839853

EXP

1
这是一条美丽的马赛克