PHP session反序列化漏洞
/0x01 PHP session序列化机制
当session_start()被调用或者php.ini中session.auto_start为1时,PHP内部调用会话管理器,访问用户session被序列化以后,存储到指定目录(默认为/tmp)。
session序列化及反序列化处理器
PHP 内置了多种处理器用于存取 $_SESSION 数据时会对数据进行序列化和反序列化,常用的有以下三种,对应三种不同的处理格式:
处理器 | 对应的存储格式 |
---|---|
php | 键名 + 竖线 + 经过 serialize() 函数反序列处理的值 |
php_binary | 键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数反序列处理的值 |
php_serialize (php>=5.5.4) | 经过 serialize() 函数反序列处理的数组 |
与session存储相关的配置项
配置文件php.ini中含有这几个与session存储相关的配置项:
1 | session.save_path ="E:/wamp64/tmp" --设置session的存储路径,默认在/tmp |
PHP 提供了 session.serialize_handler 配置选项,通过该选项可以设置序列化及反序列化时使用的处理器,默认为php。如果要修改为其他的引擎,只需要添加代码ini_set(‘session.serialize_handler’, ‘需要设置的引擎’),如下所示:
1 |
|
存储机制
php中的session中的内容并不是放在内存中的,而是以文件的方式来存储的,存储方式就是由配置项session.save_handler
来进行确定的,默认是以文件的方式存储。
存储的文件是以sess_sessionid
来进行命名的,文件的内容就是session值的序列话之后的内容。
下面用个简单的Demo看看存储的形式:
example.php,这时是使用默认的处理器即PHP:
1 |
|
可在session.save_path对应路径下看到一个新生成的session文件,这里名为sess_cj15cikdujk6uv3bdq6qvonbe7,可以看到存储格式为:键名 + 竖线 + 经过 serialize() 函数反序列处理的值
添加一行代码修改处理器为php_serialize:
1 |
|
格式:经过serialize()函数反序列处理的数组
修改处理器为php_binary:
1 |
|
可以看到:键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数反序列处理的值
0x02 PHP session反序列化漏洞
PHP session反序列化漏洞,简单点说,就是当网站序列化并存储Session与反序列化并读取Session的方式不同时就可能导致session反序列化漏洞的产生。
漏洞Demo
save.php,和前面的demo一样,这里用的是php_serialize处理器:
1 |
|
session_vul.php,这里用的是默认的php处理器,可以不添加该行:
1 |
|
访问包含恶意构造序列化对象的URL:
1 | save.php?m7=|O:7:"mi1k7ea":1:{s:1:"a";s:10:"phpinfo();";} |
打开session文件可看到序列化存储的内容,再访问session_vul.php即可看到php代码被执行了:
这是因为php引擎会以|作为key和value的分隔符,将a:1:{s:2:”m7”;s:45:”作为SESSION的key,将O:7:”mi1k7ea”:1:{s:1:”a”;s:10:”phpinfo();”;}作为value,然后进行反序列化,就会实例化mi1k7ea对象,最后就会执行__destruct()函数中的eval()方法,相当于执行如下:
1 | $_SESSION['m7'] = new mi1k7ea(); |
0x03 题目
这里网上看的一道session反序列化题目,在此复现一下。
三个PHP文件如下:
class.php
1 |
|
i.php
1 |
|
phpinfo.php
1 |
|
可以看到,i.php中用的是php处理器。
在php.ini中的关键配置,注意配置中的session.serialize_handler:
1 | session.serialize_handler = php_serialize |
可以访问phpinfo.php查看配置信息:
默认是采用php处理器处理session,session.upload_progress.cleanup配置为Off,session.upload_progress.enabled配置为On。
说下session.upload_progress.enabled,当它为开启状态时,PHP能够在每一个文件上传时监测上传进度。当一个上传在处理中,同时POST一个与php.ini中设置的session.upload_progress.name同名变量时,上传进度就可以在\$_SESSION中获得。当PHP检测到这种POST请求时,它会在\$_SESSION中添加一组数据, 索引是session.upload_progress.prefix与 session.upload_progress.name连接在一起的值。
当前代码的话没有向服务器提交数据,但是现在session.upload_progress.enabled是开启的,所以可以通过上传文件,从而在session文件中写入数据。
也就是说,利用点是通过session.upload_progress.enabled来上传文件向session文件中写入php_serialize处理器格式的内容,从而与i.php中php处理器不同进而造成session反序列化漏洞的存在。
poc.php,用于生成序列化poc,在foo1中的构造函数中定义\$varr的值为foo2的实例,在foo2中定义\$obj为foo3的实例,在foo3中定义\$varr的值为system(‘whoami’);:
1 |
|
form.html,一个向i.php提交POST请求的表单文件,其中包括PHP_SESSION_UPLOAD_PROGRESS变量:
1 | <form action="http://127.0.0.1/i.php" method="POST" enctype="multipart/form-data"> |
Burpsuite截断该form.html发送的POST请求,在PHP_SESSION_UPLOAD_PROGRESS一栏中的值加上poc.php生成的poc就能够成功执行命令了:
1 | |O:4:"foo1":1:{s:4:"varr";O:4:"foo2":2:{s:4:"varr";s:1:"1";s:3:"obj";O:4:"foo3":1:{s:4:"varr";s:19:"system("ipconfig");";}}} |
换其他命令的话直接换poc.php生成的poc即可: