php反序列化之Phar反序列化漏洞

本文深入探讨了PHP中的Phar文件格式,包括其用途、三种归档格式以及如何开启和创建Phar文件。重点讲解了Phar文件的序列化元数据存储方式,如何在文件系统函数中利用phar://伪协议触发反序列化漏洞,并通过实例展示了如何构造恶意的Phar文件。此外,还提出了利用图片文件上传绕过限制的攻击场景。

之前写的两篇文章都太理想化,真实的代码谁写成$data = unserialize($_POST['a']),直接让你反序列化,在实际的PHP程序中,主要是反序列化链(pop链),如thinkphp、Laravel的反序列化链的漏洞和利用phar://伪协议完成反序列化漏洞。今天就来学习一个phar文件序列化漏洞。

phar

phar是一个合成词,由PHP 和 Archive构成,可以看出它是php归档文件的意思。一个php应用程序往往是由多个文件构成的,如果能把他们集中为一个文件来分发和运行是很方便的。于是在PHP5.3之后支持了类似Java的jar包,名为phar。用来将多个PHP文件打包为一个文件。Phar 归档可由 PHP 本身处理,因此不需要使用额外的工具来创建或使用,使用php脚本就能创建或提取它。

三种phar归档文件格式:

tar归档、zip归档、phar归档,前两种执行需要php安装Phar 扩展支持,用的也比较少。phar格式归档文件可以直接执行,它的产生依赖于Phar扩展,由自己编写的php脚本产生。

开启phar

首先需要修改php.ini配置将phar的readonly关闭,默认是不能写phar包的,include是默认开启的。

phar.readonly => On

归档project目录下的所有文件:

下面这个文件与project文件夹同级目录

//产生一个yunke.phar文件
$phar = new Phar('yunke.phar', 0, 'yunke.phar');
// 添加project里面的所有文件到yunke.phar归档文件
$phar->buildFromDirectory(dirname(__FILE__) . '/project');
//设置执行时的入口文件,第一个用于命令行,第二个用于浏览器访问,这里都设置为index.php
$phar->setDef
### PHAR反序列化漏洞原理 PHAR是一种PHP存档格式,允许开发者创建自包含的应用程序或库。当处理PHAR文件时,如果应用程序错误地信任并加载了由攻击者控制的PHAR文件,则可能导致反序列化漏洞的发生。 在PHP环境中,`unserialize()`函数用于将存储于字符串中的表示转换成对应的变量结构。然而,在处理来自不受信源的数据时调用此函数是非常危险的行为。由于PHAR协议处理器会自动尝试解码元数据部分作为序列化的对象数组,因此即使没有显式的`unserialize()`调用也可能触发该行为[^1]。 例如,考虑如下代码片段: ```php <?php $pharFile = 'phar://' . $_GET['file']; include $pharFile; ?> ``` 上述代码中并没有直接使用`unserialize()`,但如果传入了一个恶意构造的PHAR文件路径给`$_GET['file']`参数,仍然可能引发反序列化操作,并最终导致远程命令执行等问题。 ### 防护措施 为了避免因PHAR反序列化而带来的风险,建议采取以下几种策略来增强系统的安全性: - **禁用不必要的功能**:除非确实需要支持PHAR档案的功能,否则应通过配置关闭对PHAR的支持。可以通过修改`php.ini`设置`phar.readonly=On`强制使所有打开的操作只读模式运行,从而防止写入新的PHAR文件以及潜在的风险。 - **严格验证输入**:对于任何形式的用户提交内容都应当实施严格的校验机制,尤其是涉及到文件名、URL等敏感信息的地方更要注意防范特殊字符注入的可能性。确保只有合法且预期范围内的值才能进入后续逻辑流程当中去。 - **采用白名单方式管理类定义**:限制哪些特定类型的对象能够被序列化/反序列化出来,而不是盲目接受任意种类的对象实例。这有助于减少未知风险点的存在几率。 - **更新依赖项至最新稳定版**:定期审查项目所使用的第三方组件列表,及时跟进官方发布的补丁包以修补已知的安全缺陷。 - **启用RASP实时监控保护**:引入Runtime Application Self-Protection (RASP) 技术可以在应用层面对可疑活动作出即时响应,阻止非法请求到达业务核心之前即刻拦截下来[^3]。 ### 实际案例展示 下面给出一段简单的测试代码用来说明如何构建一个存在PHAR反序列化隐患的服务端脚本及其相应的修复方案: #### 存在安全隐患的原始版本 ```php // test.php class Test { public function __destruct() { echo "Destructing...\n"; if(isset($_GET["cmd"])) { system($_GET["cmd"]); // 危险之处在于这里接收未经净化过的外部指令 } } } if(isset($_GET["data"])){ @unserialize(urldecode($_GET["data"])); // 不加甄别的反序列化过程埋下了祸根 } ``` #### 安全改进后的版本 ```php // secure_test.php class SecureTest extends SplFileInfo { // 继承SplFileInfo使得能更好地兼容phar://伪协议下的正常工作流 private $__cmd; protected function __construct($path){ parent::__construct($path); $this->__cmd = null; // 初始化私有属性,默认为空值 } final public static function createFromPath(string $path): self{ return new self($path); // 工厂方法提供统一入口点的同时也便于后期维护扩展 } public function __wakeup(){ throw new Exception("Deserialization not allowed!"); // 禁止反序列化恢复状态变更 } public function executeCmd(?string $command=null){ if(is_null($command)){ return false; } if(!preg_match('/^[a-zA-Z]+$/', trim($command))){ die('Invalid command.'); } passthru(escapeshellcmd(trim($command))); // 对待执行语句做进一步转义处理后再交给底层API } } try{ if(array_key_exists('action', $_SERVER) && array_key_exists('filename', $_GET)){ $safeObj = SecureTest::createFromPath($_GET['filename']); if(file_exists((string)$safeObj)){ include_once ((string)$safeObj); }else{ http_response_code(404); exit(); } } }catch(Throwable $e){ error_log($e->getMessage()); header("HTTP/1.1 500 Internal Server Error"); exit(); } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值