php反序列化漏洞入门知识

目录

什么是php序列化?

serialize()实例

数组反序列化实例

对象反序列化实例 

unserialize()实例 

数组序列化实例

对象序列化实例

序列化字符串格式解析 

变量类型

public、private、protected序列化后的区别

魔术方法

常见的魔术方法

方法调用实例

__sleep()

__wakeup()

__toString()

反序列化漏洞原理

参考链接


什么是php序列化?

所用到的两个函数

serialize()     //函数用于序列化对象或数组,并返回一个字符串。
unserialize()   //函数用于将通过 serialize() 函数序列化后的对象或数组进行反序列化,并返回原始的对象结构。

 

serialize()实例

数组序列化实例

将一组数组序列化后,获得一串字符串


<?php
    highlight_file(__FILE__);
    $a = array('a' => 'Apple' ,'b' => 'banana' , 'c' => 'Coconut');
    $s = serialize($a);
    echo $s;
    //a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";} 
?>

 

对象序列化实例 

将一个对象序列化后,获得一串字符串

<?php
    highlight_file(__FILE__);
    class test_class{ //创建类
		var $test = '123'; //在类中新建一个test变量
	}
	
	$class1 = new test_class; //创建对象
	$class_str = serialize($class1);//序列化对象
	echo $class_str;//输出序列化后的字符串
//O:10:"test_class":1:{s:4:"test";s:3:"123";}
?>

unserialize()实例 

数组反序列化实例

将一个标准格式的序列化字符串反序列化,会获得以下数组

<?php
    highlight_file(__FILE__);
    $str = 'a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";} ';
	$un_str=unserialize($str);	
	var_dump($un_str);	    
?>

 

对象反序列化实例

将一个标准格式的序列化字符串反序列化,会获得一个新的对象

<?php
    highlight_file(__FILE__);
    $str = 'O:10:"test_class":1:{s:4:"test";s:3:"123";}';
    $un_str=unserialize($str);
    var_dump($un_str);  
?> 

序列化字符串格式解析 

O:10:"test_class":1:{s:4:"test";s:3:"123";}

对象类型:类名称长度类名:类中变量个数:{变量:变量名长度:变量名; 变量类型变量内容长度:变量内容}

 

a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";}

数组类型:数组个数:{变量:变量名长度变量名变量类型变量内容长度变量内容;.....}

 

  • 序列化类型存在两种:
    • 对象类型
    • 数组类型
  • 可以看出,大括号内,两个分号(;)为一组,两个分号代表着一个变量中的内容。
    • {s:4:"test";s:3:"123";}

 

变量类型

不同的存储类型,序列化后会变换成不同的字母

a - array                  b - boolean  
d - double                 i - integer
o - common object          r - reference
s - string                 C - custom object
O - class                  N - null
R - pointer reference      U - unicode string

public、private、protected序列化后的区别

  • public(公共的):在本类内部、外部类、子类都可以访问
  • private(私人的):只有本类内部可以使用
  • protected(受保护的):只有本类或子类或父类中可以访问

依照上面3个修饰符序列化后,获得以下结果:

O:10:"test_class":3:{

s:6:"test_1";s:3:"str";

s:18:"test_classtest_3";i:123;

s:9:"*test_2";i:123;

}

 

  • public:序列化后基本无变化
  • private:序列化后,会在变量名前加上类的名字,变量名长度也会增加
  • protected:序列化后,变量名前会多了个 * ,但为什么变量名长度会变成9个字节?
    • 其实protected属性序列化的时候格式是%00*%00成员名

 

魔术方法

在利用对PHP反序列化进行利用时,经常需要通过反序列化中的魔术方法,检查方法里有无敏感操作来进行利用。

PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法。

常见的魔术方法

__construct()//创建对象时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当脚本尝试将对象调用为函数时触发
__sleep() //此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。
__wakeup() //经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。

php魔术方法官方文档 

 

 

方法调用实例

__sleep()

serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。

__wakeup()

unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。

__toString()

__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。

反序列化漏洞原理

当反序列化 unserialize() 的参数可控时,我们可以构造序列化字符串,从而修改对象内部的变量甚至函数。

案例:

<?php
error_reporting(0);
show_source(__FILE__);
class c{
	public $str = 111;
	
	function __wakeup(){
		echo $this->str;
		
	}
}

$str = $_GET['str'];
if (isset($str)){
	unserialize($str);
	}
?>

当我们可以控制反序列化时,我们将可以修改类中变量的内容

poc:

?str=O:1:"c":1:{s:3:"str";s:3:"123";}

当然不会那么简单,只修改值的内容而已,我们还能打个xss

poc:

?str=O:1:"c":1:{s:3:"str";s:24:"<script>alert()</script>";}

当然这个只是一个简单的案例,下一次再讲解php反序列化进阶知识。

 

参考链接

https://chybeta.github.io/2017/06/17/%E6%B5%85%E8%B0%88php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/

https://xz.aliyun.com/t/3674

https://xz.aliyun.com/t/6753

https://xz.aliyun.com/t/7366

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 1024 设计师:白松林 返回首页