刘功瑞的博客

有一天你突然惊醒,发现这一切,都只不过是一场梦。

CTF PHP反序列化

php反序列化

一、序列化

所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变回php原来的值。 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。 --php官方文档

二、魔术方法

php官方文档中关于魔术方法的部分

1、构造函数和析构函数

  • __construct()
    具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。

  • __destruct()
    析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。

也就是说进行反序列化时,完成的就是从字符串创建新对象的过程,刚开始就会调用__construct(),而对象被销毁时,例如程序退出时,就会调用__destruct()

2、__sleep()和__wakeup()

1.png

3、__toString()

1.png

4、__set(), __get(), __isset(), __unset()

1.png

5、其他

__call(), __callStatic(), __invoke(), __set_state(), __clone(), __debugInfo()等和序列化没有多大关系,详情参考官网

三、序列化细节

1、序列的含义

例如:O:4:"user":2:{s:3:"age";i:18;s:4:"name";s:3:"LEO";}
O代表对象;4代表对象名长度;2代表2个成员变量;其余参照如下

类型结构
Strings:size:value;
Integeri:value;
Booleanb:value;(保存1或0)
NullN;
Arraya:size:{key definition;value definition;(repeated per element)}
ObjectO:strlen(object name):object name:object size:{s:strlen(property name):property name:property definition;(repeated per property)}

2、public、protected、private下序列化对象的区别

  • public变量
    直接变量名反序列化出来

  • protected变量
    \x00 + * + \x00 + 变量名

  • private变量
    \x00 + 类名 + \x00 + 变量名

四、反序列化的利用

  • __wakeup失效
    当序列化字符串中,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup()的执行
    例:
    O:4:"Demo":1:{s:10:"Demofile";s:16:"f15g_1s_here.php";} O:4:"Demo":2:{s:10:"Demofile";s:16:"f15g_1s_here.php";}
    __wakeup()函数失效引发漏洞(CVE-2016-7124)

  • 使用+绕过正则
    例:
    preg_match('/[oc]:\d+:/i', $var) O:4:"Demo":1:{s:10:"Demofile";s:16:"f15g_1s_here.php";} O:+4:"Demo":1:{s:10:"Demofile";s:16:"f15g_1s_here.php";}
    代码审计Day11 - unserialize反序列化漏洞

五、Session序列化问题

https://bugs.php.net/bug.php?id=71101

PHP内置了多种处理器用于存储$_SESSION数据时会对数据进行序列化和反序列化,常用的有以下三种,对应三种不同的处理格式:

处理器对应的存储格式
php键名 + 竖线 + 经过serialize()函数反序列化处理的值
php_binary键名的长度对应的ASCII字符 + 键名 + 经过serialize()函数反序列化处理的值
php_serialize(php>=5.5.4)经过serialize()函数反序列处理的数组

配置选项 session.serialize_handler,通过该选项可以设置序列化及反序列化时使用的处理器。

如果PHP在反序列化存储的$_SEESION数据时的使用的处理器和序列化时使用的处理器不同,会导致数据无法正确反序列化,通过特殊的伪造,甚至可以伪造任意数据。

当存储是php_serialize处理,然后调用时php去处理,如果这时注入的数据时a=|O:4:"test":0:{},那么session中的内容是a:1:{s:1:"a";s:16:"|O:4:"test":0:{}";},那么a:1:{s:1:"a";s:16:"会被php解析成键名,后面就是一个test对象的注入

当配置选项session.auto_start=Off,两个脚本注册session绘画是使用的序列化处理器不同,就会出现安全问题

<form action="upload.php" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="ryat" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

The key of stored in the session will look like this:

$_SESSION["upload_progress_ryat"]

ryat的部分可以注入自己的代码,如ryat|序列化字符串

六、Phar反序列化

https://blog.ripstech.com/2018/new-php-exploitation-technique/
PHAR反序列化拓展操作总结
SUCTF 2019 出题笔记 & phar 反序列化的一些拓展
利用 phar 拓展 php 反序列化漏洞攻击面
创建一个Phar文件

// create new Phar$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); ? >');// add object of any class as meta dataclass AnyClass {}
$object = new AnyClass;
$object->data = 'rips';
$phar->setMetadata($object);
$phar->stopBuffering();

如果现在通过phar://包装器对我们现有的Phar文件执行文件操作,则其序列化元数据将被反序列化。这意味着我们在元数据中注入的对象被加载到应用程序的范围中。如果此应用程序具有已命名的类AnyClass并且具有魔术方法__destruct()或已__wakeup()定义,则会自动调用这些方法。这意味着我们可以在代码库中触发任何析构函数或唤醒方法。更糟糕的是,如果这些方法对我们注入的数据进行操作,那么这可能会导致进一步的漏洞。

//通过Phar文件进行PHP对象注入
class AnyClass {
    function __destruct() {
        echo $this->data;
    }
}
// output: rips
include('phar://test.phar');

phar://在任何文件操作中都会为包装器触发unserialize,可利用函数包括

ìnclude($_GET['file']);
fopen($_GET['file']);
file_get_contents($_GET['file']);
file($_GET['file']);
file_exists($_GET['file']);
md5_file($_GET['file']);
filemtime($_GET['file']);
filesize($_GET['file']);

发表评论:

Powered By Z-BlogPHP 1.5.2 Zero

Copyright www.liugongrui.com.All Rights Reserved.