【反序列化漏洞-02】PHP反序列化漏洞原理测试及魔术方法总结

目录

  • 1 简介
  • 2 PHP反序列化漏洞
    • 2.1 测试环境
    • 2.2 测试过程
  • 3 PHP魔术方法归纳
  • 4 总结

1 简介

PHP反序列化漏洞也叫PHP对象注入,是一个非常常见的漏洞,这种类型的漏洞虽然有些难以利用,但一旦利用成功就会造成非常危险的后果。
漏洞形成的根本原因是程序没有对用户输入的反序列化字节流进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、GetShell等一系列不可控的后果。(在反序列化过程中,会触发代码执行。)
反序列化漏洞并非是PHP所特有的,也存在与java、Python等语言中,原理基本相同。

PHP中的序列化与反序列化,基本都是围绕serialize()和unserialize()两个函数展开的。

2 PHP反序列化漏洞

2.1 测试环境

服务器:在虚拟机中安装win2008及phpstudy,参考《【语言环境】WAMP环境部署及优化—以win2008R2SP1为操作系统》。

客户端:真实机浏览器。

2.2 测试过程

(1)测试1

  • 1)在服务器网站根目录下新建一个文件夹serialize_unserialize,再在该文件夹下新建一个txt文件,输入以下内容,并重命名为unserialize_vun.php。
<meta charset = "utf-8">
<?php
//定义一个stu类,类中有4个属性,暂未定义方法。
class test{
	public $str='gzz';
	function __destruct(){
		echo "This is function __destruct()";
		@eval($this->str);
	}
}

//创建对象并序列化后输出
$test1 = new test();
echo "序列化后采用echo输出:
"
; echo serialize($test1); echo "
"
; //输入序列化后的字符串,并采用凡序列化后采用var_dump输入 $obj=$_GET['obj']; echo "反序列化后采用var_dump输出:
"
; var_dump(unserialize($obj));
  • 2)浏览器访问该网页,攻击者构造输入参数?obj=O:4:"test":1:{s:3:"str";s:5:"hello";},网页显示如下,含有两句“ This is function __destruct()”,说明函数被调用两次。注意,当销毁实例化类(对象)的时候,__destruct()函数会被自动调用,

【反序列化漏洞-02】PHP反序列化漏洞原理测试及魔术方法总结_第1张图片

  • 3)浏览器访问该网页,攻击者构造输入参数?obj=O:4:"test":1:{s:3:"str";s:10:"phpinfo();";},网页显示如下,可以看到phpinfo()函数被成功执行。

【反序列化漏洞-02】PHP反序列化漏洞原理测试及魔术方法总结_第2张图片

(2)测试2

  • 1)将unserialize_vun.php文件修改如下,将新建test类对象的语句注释掉,再次实验。
<meta charset = "utf-8">
<?php
//定义一个stu类,类中有4个属性,暂未定义方法。
class test{
	public $str='gzz';
	function __destruct(){
		echo "This is function __destruct()";
		@eval($this->str);
	}
}

//创建对象并序列化后输出
//$test1 = new test();
//echo "序列化后采用echo输出:
";
//echo serialize($test1); echo "
"
; //输入序列化后的字符串,并采用凡序列化后采用var_dump输入 $obj=$_GET['obj']; echo "反序列化后采用var_dump输出:
"
; var_dump(unserialize($obj));
  • 2)浏览器同样输入参数?obj=O:4:"test":1:{s:3:"str";s:5:"hello";},网页显示如下,含有1句“ This is function __destruct()”,说明函数被调用1次。注意,当销毁实例化类(对象)的时候,__destruct()函数会被自动调用,
    【反序列化漏洞-02】PHP反序列化漏洞原理测试及魔术方法总结_第3张图片

  • 3)浏览器同样输入参数?obj=O:4:"test":1:{s:3:"str";s:10:"phpinfo();";}访问,网页显示如下,可以看到同样能执行phpinfo()函数,因此可以判断输入对象成功反序列化为test类对象,并自动执行了__destruct()函数。

【反序列化漏洞-02】PHP反序列化漏洞原理测试及魔术方法总结_第4张图片
(3)测试3

  • 1)将unserialize_vun.php文件修改如下,将最后一行语句修改如下,再次实验。
<meta charset = "utf-8">
<?php
//定义一个stu类,类中有4个属性,暂未定义方法。
class test{
	public $str='gzz';
	function __destruct(){
		echo "This is function __destruct()";
		@eval($this->str);
	}
}

//创建对象并序列化后输出
//$test1 = new test();
//echo "序列化后采用echo输出:
";
//echo serialize($test1); echo "
"
; //输入序列化后的字符串,并采用凡序列化后采用var_dump输入 $obj=$_GET['obj']; //echo "反序列化后采用var_dump输出:
";
//var_dump(unserialize($obj)); $obj1=unserialize($obj); ?>
  • 2)浏览器同样输入参数?obj=O:4:"test":1:{s:3:"str";s:5:"hello";},网页显示如下,含有1句“ This is function __destruct()”,说明函数被调用1次。
    【反序列化漏洞-02】PHP反序列化漏洞原理测试及魔术方法总结_第5张图片
  • 3)浏览器同样输入参数?obj=O:4:"test":1:{s:3:"str";s:10:"phpinfo();";}访问,,网页显示如下,可以看到同样能执行phpinfo()函数,因此可以判断输入对象成功反序列化为test类对象,并自动执行了__destruct()函数。
    【反序列化漏洞-02】PHP反序列化漏洞原理测试及魔术方法总结_第6张图片

在上述实例中,有以下几点需要理解:

  • 我们发现,当销毁实例化类的时候,__destruct()函数会被自动调用,并输出字符串This is function__destruct()。
  • 默认情况下,PHP仅仅释放对象属性所占用的内存并销毁对象相关的资源.,__destruct()允许你在使用一个对象之后执行任意代码来清除内存,当PHP决定你的脚本不再与对象相关时,__destruct()将被调用.,在一个函数的命名空间内,这会发生在函数return的时候,对于全局变量,这发生于脚本结束的时候,如果你想明确地销毁一个对象,你可以给指向该对象的变量分配任何其它值,通常将变量赋值勤为NULL或者调用unset。

3 PHP魔术方法归纳

(1)以 __ 开头的的方法,是PHP中的魔术方法,在特定情况下会被自动调用。主要魔术方法及其触发条件如下:

  • __construct():构造方法,当一个对象被创建时调用此方法.
  • __destruct():析构方法,PHP将在对象被销毁前(即从内存中清除前)调用这个方法
  • __autoload() :使用尚未被定义的类时自动调用。通过此函数,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。
  • __call( $method, $arg_array ):在对象中调用一个不可访问方法时
  • __callStatic():在静态上下文中调用一个不可访问的方法时使用
  • __clone():使用clone方法复制一个对象时
  • __invoke():当尝试调用函数的方式调用一个对象时
    - __get( $property ):从不可访问的属性中读取数据
  • __set( $property, $value ):给一个未定义的属性赋值时调用
  • __isset( $property ):当在一个未定义的属性上调用isset()函数时调用此方法
  • __unset( $property ):当在一个未定义的属性上调用unset()函数时调用此方法
  • __toString():在将一个对象转化成字符串时自动调用
  • __sleep():序列化对象前调用(其返回需要是一个数组),详见补充说明
  • __wakeup():反序列化恢复对象前调用,详见补充说明
  • __set_state():当调用var_export()时,这个静态方法会被调用
  • __invoke():当尝试以调用函数的方式调用一个对象时,__invoke 方法会被自动调用

(2)__sleep()和__wakeup()的补充说明

  • serialize() 检查类中是否有魔术名称 __sleep的函数。如果这样,该函数将在任何序列化之前运行。它可以清除对象并应该返回一个包含有该对象中应被序列化的所有变量名的数组。
  • 使用 __sleep 的目的是关闭对象可能具有的任何数据库连接,提交等待中的数据或进行类似的清除任务。此外,如果有非常大的对象而并不需要完全储存下来时此函数也很有用。
  • 相反地,unserialize() 检查具有魔术名称 __wakeup 的函数的存在。如果存在,此函数可以重建对象可能具有的任何资源。
  • 使用 __wakeup 的目的是重建在序列化中可能丢失的任何数据库连接以及处理其它重新初始化的任务。

4 总结

(1)理解PHP反序列化漏洞的触发原理;
(2)了解PHP相关的魔术方法及调用情况。

你可能感兴趣的:(#,入门07,Web安全之渗透测试,反序列化漏洞)