php反序列化

每天都要加油努力  ------    2024-01-03    17:27:33

前言:php反序列化是CTF比赛中的重点,考验选手对反序列化的理解程度


0x00 序列化与反序列化概念

序列化将对象转化为可传输的字节序列的过程

反序列化:把字节序列转化为对象的过程

序列化与反序列化的最终目的:实现对象的跨平台存储,网络传输

反序列化漏洞产生的原因就是反序列化处的参数用户可控,服务器接收我们序列化后的字符串并且未经过滤把其中的变量放入一些魔术方法里面执行,这就很容易产生漏洞。


0x01 反序列化与序列化的过程

例如下面的序列化过程,分别将整形,字符串,数组用serialize序列化:


fig:


将其unserialize反序列化还原

fig:

稍微复杂点的序列化过程:,将对象反序列化,其中O代表的是object,对象的意思

fig:


0x02 魔术方法Magic Function

PHP中有一类特殊的方法叫做“Magic Function”,即魔术方法,PHP将所有以__(双下划线开头的类方法保留为魔术方法,初识者可能会很懵,但是学过面向对象后应该很好理解。主要重点关注:

__construct():当对象被创建(new)的时候会自动调用。但在反序列化的时候不会自动调用。
__destruct():当对象被销毁的时候会自动调用。
__wakeup():当unserialize()时会自动调用.
注:CVE-2016-7124当对象属性的个数大于真实属性的时候,会跳过__wakeup函数
__toString():当对象以字符串被输出的时候自动调用
注:__toString()触发方式比较多:

echo ($obj) / print($obj) 打印时会触发
1、反序列化对象与字符串连接时
2、反序列化对象参与格式化字符串时
3、反序列化对象与字符串进行比较时(PHP进行比较的时候会转换参数类型)
4、反序列化对象参与格式化SQL语句,绑定参数时
5、反序列化对象在经过php字符串函数,如 strlen()、addslashes()时
6.在in_array()方法中,第一个参数是反序列化对象,第二个参数的数组中有toString返回的字符串的时候toString会被调用
7.反序列化的对象作为 class_exists() 的参数的时候
8.声明的变量赋值为对象后做弱比较的时候触发__toString
__sleep() //执行serialize()时,先会调用这个函数
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据或者不存在这个键都会调用此方法
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当尝试将对象调用为函数时触发 例如对象名是test,调用test()

以下我将一个一个举例子,方便理解

<?php
class obj{
    public $val="high!";
    public function __construct()
    {
        echo "class construct!\n";
    }
    public function __destruct(){
        echo "class destruct!\n";
    }
}

$obj=new obj();

在这个php文件里我创建了一个类,我new出了一个对象,但是我没有调用任何方法,它却自动的输出了,说明__construct(构造函数)__destruct(析构函数) 是自动调用的。其中构造函数在对象创建的时候就自动调用,析构函数在php脚本运行完之后自动调用。

 剩余的我将有时间就更新。

 

 


0x03 序列化特征值

fig:

fig:

fig:

0x04 gadget构造

什么是gadget?

gadget是由不同组件(类,函数,变量)组成的一条可用攻击链

什么是反序列化漏洞中的gadget构造

通过寻找PHP应用中的类,函数,变量来构造出一个完整的调用链在反序列化中实现攻击效果

看下面的代码,怎么触发systen函数呢

fig:

 

思路:我们可以通过unserialize除法魔术方法的__wakeup(),再触发construct函数,最终触发system函数

0x05 命令空间中的反序列化

什么是命名空间---> 一种封装事物的方法

命名空间主要为了解决编程时遇到的两类问题:

1.用户编写的代码与PHP内部的类/函数/常量和第三方类/函数/常量之间的名字冲突

2.为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名的名称,提高源代码的可读性

如何定义一个命名空间,通过关键字namespace声明,且必须放在所有代码前面
<?php
namespace test;
const test_const=1;
class test_class{}
function test_func(){}
?>


只有类,接口,函数,变量受命名空间影响

 

与目录和文件的关系很像,PHP命名空间也允许层次化的命名空间的名称
<?php
namespace test\sub\level

const test_const=1;
class test_class{}
function test_func(){}

$a=new \test\sub\level\test_class();
?>
可以使用use关键字引入不同的命名空间
<?php
namespace test sub level;
use namespace test\sub level1;
const test _const = 1;
class test_class{}
function test func(){}
$a = new \test\sub\llevel1\test_class();
?>

 

如果在命名空间的定义结束之后,想重新在全局域定义类等信息,则可以使用全局非命名空间
<?php
namespace test\subllevel;
use namespace test subllevel1:
const test const = 1;class test classfunction test func()f)]
namespace{
$a = new testlsub level1 test class(); //则会在
}

fig:

 

posted @ 2024-01-03 17:42  AllFalls  阅读(43)  评论(0编辑  收藏  举报