反序列化漏洞


一、为什么要进行序列化

百度百科上序列化定义是,将对象的状态信息转换为可以存储或传输的形式(字符串)的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区(Redis 键值对数据库,常用于做缓存)。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。

简单的说,序列化就是把一个对象变成可以传输的字符串,可以以特定的格式在进程之间跨平台、安全的进行通信。

EX:创建PHP类和对象

<?php 
class Stutent{
	public $name;
	public $age;
	public $sex;
	public $score;
}

$sxk=new Stutent();
$lyl=new Stutent();

$sxk->name="sxk";
$sxk->age=21;
$sxk->sex=true;
$sxk->score=90;


$lyl->name="lyl";
$lyl->age=21;
$lyl->sex=false;
$lyl->score=95;

echo $lyl->name."' s age =".$lyl->age;
echo "<hr />";
echo $sxk->name."' s age =".$sxk->age;
echo "<hr />";
var_dump($lyl);
?>

二、PHP 反序列化漏洞

2.1 概述

PHP反序列化漏洞也叫PHP对象注入,是一个非常常见的漏洞,这种类型的漏洞虽然有些难以利用,但一旦利用成功就会造成非常危险的后果。漏洞的形成的根本原因是程序没有对用户输入的序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell等一系列不可控的后果。

反序列化漏洞并不是PHP特有,也存在于Java、Python等语言之中,但其原理基本相通。

PHP 中的序列化与反序列化,基本都是围绕serialize()unserialize() 两个函数展开的。在介绍这两个函数之前,我们来看一个简单的例子。

2.2 简单的序列化例子

我们可以用json(格式化的字符串)格式数据的编码与解码,来理解序列化与反序列化的过程。虽然json 数据与反序列化漏洞没有什么关系,但是这个例子会帮助我们理解。

测试代码如下:⚠️:echo 不能输出数组

我们定义一个数组,数组属于抽象的数据结构,为了方便跨平台传输数据,可以将其进行json 编码。json 格式的数据是以键值对的形式出现的。结果如下:

EX:json ——放便跨平台传输的数据结构

<?php
$stu=array('name'=>'AJEST','age'=>18,'SEX'=>true,'score'=>89.9);
echo $stu;
echo "<hr />";
$stu_json=json_encode($stu);
echo $stu_json;
?>

2.3 序列化Demo

序列化会将一个抽象的对象转换为字符串。

我们可以写一个Demo 来说明序列化的过程,首先创建一个类,代码如下:

<?php
class Stu{
	    public $name;
	    public $sex;
	    public $age;
	    public $score;
}
?>

类名是Stu,该类中有四个变量(关于变量的性质,我们这里不讨论。接下来,我们可以将这个类实例化,也就是创建一个对象,并给对象中变量赋值。代码如下

<?php
	include "classStu.php";
	$stu1 = new Stu();
	$stu1->name = "AJEST";     
	$stu1->sex = true;     
	$stu1->age = 18;     
	$stu1->score = 89.9;     
	echo serialize($stu1);
?>

最后我们使用serialize(),将$stu1 这个对象序列化成一个字符串。这样的字符串。就很容易传输和存储了。如下:

O:3:"Stu":4:  //O 代表Object 对象;3类名有三个字符;对象中有4个变量
{s:4:"name";s:5:"AJEST";
s:3:"sex";b:1;
s:3:"age";i:18;
s:5:"score";d:89.900000000000006;}

同样,我们可以使用unserialize() 函数,将字符串反序列化为一个对象。由于字符串中含有双引号,所以此处可以使用定界符的方法定义字符串。代码如下:

<?php
include "classStu.php";
	$stu1=<<<STR
	O:3:"Stu":4:{s:4:"name";s:5:"AJEST";s:3:"sex";b:1;s:3:"age";i:18;s:5:"score";d:89.900000000000006;}
	STR;
	$stu1=unserialize($stu1);
	var_dump($stu1);
?>

运行这个脚本文件,我们可以看到反序列化后的对象。

EX:序列化与反序列化Demo

<?php
class Stutent{
	public $name;
	public $age;
	public $sex;
	public $score;
}

$sxk=new Stutent();

$sxk->name="sxk";
$sxk->age=21;
$sxk->sex=true;
$sxk->score=90;

echo "<hr />";
//echo $sxk;
echo "<hr />";
echo serialize($sxk);
echo "<hr />";


$str=<<<STR
O:7:"Stutent":4:{s:4:"name";s:3:"sxk";s:3:"age";i:21;s:3:"sex";b:1;s:5:"score";i:90;}
STR;
var_dump(unserialize($str));

2.4 漏洞的产生

  • 创建一个类
<?php
class Test{
    public $str='AJEST;';
    function __destruct(){
        //echo "This is function __construct()";
        @eval($this->str);
    }
}
?>
  • 创建一个类的对象,并将其序列化
$test = new Test();
echo serialize($test);

得到字符串

[O:4:"Test":1:{s:3:"str";s:6:"AJEST;";}]
  • 反序列化

将得到的序列化字符串反序列化为对象。

var_dump(unserialize($_GET['code']));
  • 反序列化注入
构造序列化字符[O:4:"Test":1:{s:3:"str";s:10:"phpinfo();";}]。
传入code 参数。phpinfo() 函数会被执行。

由以上代码,我们会发现,PHP 的反序列化漏洞需要与其他漏洞配合,比如代码执行、SQLi 等。在这个例子中,析构函数调用eval()执行`$str字符串,间接导致了命令执行。

2.5 原因分析

我们注入的字符串[phpinfo()],为什么会作为PHP 语句运行呢?观察代码,发现在类中有一个函数 __destruct() 并且这个函数调用的eval 语句,执行$this->str 变量。为什么__destruct() 没有被调用,函数内的语句就被执行了呢?

当销毁实例化类(对象)的时候,__destrutc() 函数会被自动调用。

2.6 魔术方法

__construct():创建对象时自动调用
__destruct(): 在销毁对象时自动调用
__call():在对象中调用一个不可访问方法时,__call() 会被调用
__callStatic():在静态上下文中调用一个不可访问方法时调用
__get():读取不可访问属性的值时,__get() 会被调用
__set():在给不可访问属性赋值时,__set() 会被调用
__isset():当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用
__unset():当对不可访问属性调用 unset() 时,__unset() 会被调用
__sleep():serialize() 函数会检查类中是否存在一个魔术方法__sleep() ,如果存在,该方法会鲜卑调用,然后再执行序列化操作
__wakeup():unserialize() 会检查是否存在一个 __wakeup() 方法,如果存在会先调用__wakeup方法,预先准备对象需要的资源
__toString():__toString() 方法用于一个类被当成字符串时应增氧回应
__invoke():当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用
__set_state():自PHP 5.1.0 起调用 var_export() 导出类时,此静态 方法会被调用
__clone():当复制完成时,如果定义了 __clone 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用,可用于修改属性的值。
__debugInfo():This method is called by var_dump() anobject to get the properties that should be shown .If the method isn't on an object ,then all public ,protected and private propertieswill be shown.

三、反序列化漏洞实战

3.1 typecho1.0反序列化漏洞

3.1.1 目标 typecho1.0

安装目标网站,数据库密码 root,登陆密码 admin

提示无法连接数据库:手动创建数据库。

创建数据库:typecho

整理:utf8_gerneral_ci

3.1.2 反序列化漏洞利用

  • 写exp:

typecho1.0_unserialize_exp.php

  • 上传到服务器并访问:返回base64编码的字符串。
  • 测试phpinfo()生成的POC:
  • 代码执行:

我们将以上代码赋值给__typecho_config变量

然后通过POST方式提交到链接http://172.16.132.138/typechov11/install.php?finish=

并设置 Referer= http://172.16.132.138/typecho10/

可以看到phpinfo()代码成功执行。

  • 测试一句话木马getshell:

进一步可以通过蚁剑连接 http://192.168.33.4/typecho10/shell.php

总结

反序列化漏洞是一种安全漏洞,它发生在应用程序将不可信的输入数据反序列化成对象时,如果这些数据被恶意构造,攻击者可以利用这种漏洞执行任意代码、访问敏感数据或进行其他恶意操作,这种漏洞常见于支持序列化功能的编程语言和框架中,如Java、Python和PHP等。


文章作者: 司晓凯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 司晓凯 !
  目录