WEB入门——PHP反序列化漏洞

一、序列化与反序列化

1.1 概念

序列化(Serialization): 将对象的状态信息转换为可以存储或传输的形式的过程,一般将对象转换为字节流。序列化时,对象的当前状态被写入到临时或持久性存储区(文件、内存、数据库等)。
反序列化(Deserialization): 从序列化的表示形式中提取数据,即把有序字节流恢复为对象的过程。
反序列化攻击: 攻击者控制了序列化后的数据,将有害数据传递到应用程序代码中,发动针对应用程序的攻击。

二、PHP序列化与反序列化

2.1 介绍

在PHP应用中,序列化和反序列化一般用做缓存,比如session缓存,cookie等。
常见的序列化格式:

  • 二进制格式
  • 字节数组
  • json字符串
  • xml字符串

2.2 实现方式

PHP通过string serialize ( mixed $value )mixed unserialize ( string $str ) 两个函数实现序列化和反序列化

2.2.1 数组序列化与反序列化

<?php
echo '序列化:';
$human = array('name'=>'zhangshan','age'=>20);
$str = serialize($human);
echo $str;

echo "反序列化:";
$human_1 = unserialize($str);
echo var_dump($human_1);
?>
序列化:a:2:{s:4:"name";s:9:"zhangshan";s:3:"age";i:20;}
反序列化:array(2) { ["name"]=> string(9) "zhangshan" ["age"]=> string(2) "20" }
  • 序列化格式:
  • a表示array,2表示连个元素;
  • s:4:“name” 表示第一个元素名称为字符串,长度为4,值为name;
  • s:9:"zhangshan"表示第一个元素的值为字符串,长度为9,值为zhangshan;
  • s:3:"age"表示第二个元素名称为字符串,长度为3,值为age;
  • i:20 表示第二个元素值为int型,值为20;

2.2.2 对象序列化与反序列化

<?php
class hello{
	public $var1;
	protected $var2;
	private $var3;
	function  __construct($var1,$var2,$var3){
		$this->var1=$var1;
		$this->var2=$var2;
		$this->var3=$var3;
	}
}
echo '序列化:';
$syh=new hello("xiaoming","hello",99);
$str = serialize($syh);
$str2 = urlencode($str); # URL编码
echo $str;
echo "<br>";
echo $str2;

echo "反序列化:";
$syh1 = unserialize($str);
echo "<br>";
echo var_dump($syh1);
?>
序列化:O:5:"hello":3:{s:4:"var1";s:8:"xiaoming";s:7:"*var2";s:5:"hello";s:11:"hellovar3";i:99;}
O%3A5%3A%22hello%22%3A3%3A%7Bs%3A4%3A%22var1%22%3Bs%3A8%3A%22xiaoming%22%3Bs%3A7%3A%22%00%2A%00var2%22%3Bs%3A5%3A%22hello%22%3Bs%3A11%3A%22%00hello%00var3%22%3Bi%3A99%3B%7D反序列化:
object(hello)#2 (3) { ["var1"]=> string(8) "xiaoming" ["var2":protected]=> string(5) "hello" ["var3":"hello":private]=> int(99) }
  • 序列化格式:
  • O:5:“hello”:3 ,表示类型为Object,对象名长度为5,值为hello,且有3个元素。
  • s:4:“var1”,表示第一个元素名字为字符串,长度为4,元素名字为var1。
  • s:8:“xiaoming”,表示第一个元素值为字符串,长度为8,值为xiaoming。
  • s:7:“*var2”,表示第二个元素名字为字符串,长度为7,元素为protected ,名字为var2。
  • s:5:“hello”,表示第二元素值为字符串,长度为5,值为hello。
  • s:11:“hellovar3”,表示三个元素名字为字符串,长度为11,元素为private ,名字为var3。
  • i:99,表示第三个元素值为整型,值为99。
  • public成员变量的名字直接写。public $var1;——序列化:s:4:“var1”;——URL编码:s%3A4%3A%22var1
  • protected成员变量名字前面要添加%00*%00。protected $var2;——序列化:s:7:“*var2”——URL编码:
    s%3A7%3A%22%00%2A%00var2
  • private成员变量名字前面要添加%00类名%00对。 private $var3;——序列化:s:11:“hellovar3”——URL编码:s%3A11%3A%22 %00hello%00var3
  • 序列化时,只保存成员变量,不保存方法。

2.3 常用魔术方法

魔术方法介绍
__construct()初始化类的时候,一般对于变量进行赋值
__toString()当一个对象被当作一个字符串被调用,把类当作字符串使用时触发,返回值需要为字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。
__wakeup()使用unserialize时触发,反序列化恢复对象之前调用该方法
__sleep()使用serialize时触发.该函数需要返回以类成员变量名作为元素的数组(该数组里的元素会影响类成员变量是否被序列化。只有出现在该数组元素里的类成员变量才会被序列化
__destruct()对象被销毁时触发
__invoke()当脚本尝试将对象调用为函数时触发
  • 运行情况:
    <?php
    class hello{
    	public $var1;
    	protected $var2;
    	private $var3;
    	function  __construct($var1,$var2,$var3){
    		$this->var1=$var1;
    		$this->var2=$var2;
    		$this->var3=$var3;
    		echo "__contruct<br/>";
    	}
    	function __destruct(){
    		echo "__destruct<br/>";
    	}
    	function __toString(){
    		echo "__toString<br/>";
    		return "object of class A;";
    	}
    	function __wakeup(){
    		echo "__wakeup<br/>";
    	} 
    }
    echo '序列化开始:<br>';
    $syh=new hello("xiaoming","hello",99);
    echo '序列化结束:<br>';
    $str = serialize($syh);
    $str2 = urlencode($str);
    echo $str;
    echo "<br>";
    echo $str2;
    echo "<br>";
    echo "反序列化开始:<br>";
    $syh1 = unserialize($str);
    echo "反序列化结束:<br>"; 
    echo var_dump($syh1);
    echo "<br>";
    
    ?>
    
    序列化开始:
    __contruct
    序列化结束:
    O:5:"hello":3:{s:4:"var1";s:8:"xiaoming";s:7:"*var2";s:5:"hello";s:11:"hellovar3";i:99;}
    O%3A5%3A%22hello%22%3A3%3A%7Bs%3A4%3A%22var1%22%3Bs%3A8%3A%22xiaoming%22%3Bs%3A7%3A%22%00%2A%00var2%22%3Bs%3A5%3A%22hello%22%3Bs%3A11%3A%22%00hello%00var3%22%3Bi%3A99%3B%7D
    反序列化开始:
    __wakeup
    反序列化结束:
    object(hello)#2 (3) { ["var1"]=> string(8) "xiaoming" ["var2":protected]=> string(5) "hello" ["var3":"hello":private]=> int(99) }
    __destruct
    __destruct
    

三、利用方式

3.1 __wakeup()绕过漏洞

  • 影响版本:
    • PHP before 5.6.25
    • 7.x before 7.0.10
  • 反序列化时,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup( )的执行
  • 例如:
    <?php
    class hello{
    	public $var1=10; 
    	  
    	function __wakeup(){
    		echo "__wakeup<br/>";
    	} 
    	function __destruct(){
    	echo "__destruct<br/>";
    	} 
    } 
    $syh=new hello(); 
    $str = serialize($syh);  
    echo $str ; # O:5:"hello":1:{s:4:"var1";i:10;}
    echo "<br>";
    
    unserialize('O:5:"hello":2:{s:4:"var1";i:10;}');  # 这里将 "hello":1->"hello":2
    
    ?>
    
    O:5:"hello":1:{s:4:"var1";i:10;}
    

3.2 引用绕过

  • 例如:

    <?php
    class hello{
    	public $var1;
    	public $var2;	
    	
    	function __destruct(){
    		$this->ver2 = uniqid();
    		if($var1 === $ver2){
    			echo "flag";
    		}else{
    			echo "nonono";
    		}
    	} 
    } 
    $syh=new hello(); 
    $syh->var1 = '' ;
    $syh->var2 = &$syh->var1 ;
    
    ?>
    
    flag
    
  • $syh->var2 = &$syh->var1 ,指定var2 是var1的引用,所以两个的值完全一样。

四、其他

4.1 序列化字符串中的+号

  • 在序列化字符串中的数字前可以使用+号表示正数,不影响返序列化的结果。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值