这是第二篇

0x01 基础知识

PHP的Session解析机制

对于PHP的session反序列化,最重要的就是这几个参数。

Directive 含义
session.save_handler session保存形式。默认为files
session.save_path session保存路径。
session.serialize_handler session序列化存储所用处理器。默认为php
session.upload_progress.cleanup 一旦读取了所有POST数据,立即清除进度信息。默认开启
session.upload_progress.enabled 将上传文件的进度信息存在session中。默认开启。

尤其是session.serialize_handler,默认为php。

除了php这种引擎外,还有两种

处理器名称 存储格式
php 键名 + 竖线 + 经过serialize()函数序列化处理的值
php_binary 键名的长度对应的 ASCII 字符 + 键名 + 经过serialize()函数序列化处理的值
php_serialize 经过serialize()函数序列化处理的数组
//1.php引擎
<?php
session_start()
$_SESSION['name'] = 'gungnir';
var_dump();
?>
    
//结果为:
//name|s:7:"gungnir"
    
//------------------------------------
//2.php_serialize引擎
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['name'] = 'gungnir';
var_dump();
?>
    
//结果是:
//a:1:{s:4:"name";s:7:"gungnir";}
    
    
//------------------------------------
//3.php_binary引擎
<?php
ini_set('session.serialize_handler', 'php_binary');
session_start();
$_SESSION['name'] = 'gungnir';
var_dump();
?>
    
//结果是:
//names:6:"spoock";
//由于name的长度是4,4在ASCII表中对应的就是EOT。根据php_binary的存储规则,最后就是names:6:"spoock";。

PHP的Session存储机制

对于php中的session内容一般以文件的形式存储于服务器中,而本地浏览器会存储一个与服务器中session文件对应的Cookie值,Cookie存储的是键值为“PHPSESSID”的Seeion_id值。

默认文件在/tmp/sess_PHPSESSID(PHPSESSID就是cookie中的PHPSESSID)

如果对于session有许多困惑和混淆,推荐一篇文章,我认为针对session与其他概念的梳理讲的很好。

有关 Session 的那些事儿

0x02原理

PHP中的Session的实现是没有的问题,危害主要是由于程序员的Session使用不当而引起的。

PHP在反序列化存储的$_SESSION数据时使用的引擎和序列化使用的引擎不一样,会导致数据无法正确第反序列化。

如果我们用php_serialize或php_binary在存储序列化时,在其中添加 | 符号,而用php引擎来解析时,根据php引擎解析特性,**|** 会被认为是分隔符。

这时,**|**后面的恶意序列化就会被解析。

例子:

$_SESSION['gungnir'] = '|O:8:"stdClass":0:{}';

上面的 $_SESSION 数据,在存储时使用的序列化处理器为 php_serialize,存储的格式如下:

a:1:{s:7:"gungnir";s:20:"|O:8:"stdClass":0:{}";}

读取数据时如果用的反序列化处理器不是 php_serialize,而是 php 的话,那么反序列化后的数据将会变成:

<?php
var_dump($_SESSION);
?>

array(1) {
  ["a:1:{s:7:"gungnir";s:20:""]=>
  object(stdClass)#1 (0) {
  }
}

就是我上面说的

当使用php引擎的时候,php引擎会以|作为作为key和value的分隔符,那么就会将a:1:{s:5:"ca01h";s:20:"作为SESSION的key,将O:8:"stdClass":0:{}作为value,然后进行反序列化,最后就会得到stdClass这个类。

0x03漏洞类型

按照session.auto_start是否打开分为两种。

第一种:

session.auto_start=On

当配置选项 session.auto_start=On,会自动注册 Session 会话(相当于执行了session_start()),因为该过程是发生在脚本代码执行前,所以在脚本中设定的包括序列化处理器在内的 session 相关配选项的设置是不起作用的。因此一些需要在脚本中设置序列化处理器配置的程序会在 session.auto_start=On 时,销毁自动生成的 Session 会话。然后设置需要的序列化处理器,再调用 session_start() 函数注册会话,这时如果脚本中设置的序列化处理器与 php.ini 中设置的不同,就会出现安全问题。

// foo1.php
if(ini_get('session.auto_start')) 
  session_destroy();//这里销毁了配置中session设置

ini_set('session.serialize_handler', 'php_serialize');//这里重新设置,有可能会有漏洞
session_start();

if(isset($_GET['test']))
  $_SESSION['test'] = $_GET['test'];

第二种:

当配置选项 session.auto_start=Off,两个脚本注册 Session 会话时使用的序列化处理器不同,就会出现安全问题,如下面的代码:

<?php
// foo1.php
ini_set('session.serialize_handler', 'php_serialize');
session_start();

$_SESSION['test'] = $_GET['test'];
  <?php
  // foo2.php
  session_start();
  
  class test {
      var $hi;
  
      function __wakeup() {
          echo 'hi';
      }
      function __destruct() {
          echo $this->hi;
      }
}