PHP反序列化漏洞
2023-7-3 10:21:5 Author: 小艾搞安全(查看原文) 阅读量:35 收藏

今天简单介绍一下php反序列化漏洞,其实这个漏洞我们在实战中并不常见,经常在某些CTF比赛,或者是面试的时候会问到,所以这个我们还是需要了解的。本文主要讲解的是PHP的反序列化。

一、概念

要了解反序列化漏洞,首先要了解关于序列化的一些概念。

序列化:

序列化 (serialize)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。简单来说就是将对象转换为字符串

反序列化:

反序列化和序列化刚好相反,就是将这个状态信息拿出来再进行使用。就是将字符串再转换成对象。

举个例子:

其实这个过程就好像是我们玩单机游戏,有一个存档和读档的功能,存档就相当于是序列化,将当前的一个人物或者关卡信息进行一个存档,再我们下次玩的时候,就会将这些东西再拿出来,相当于一个反序列化。再或者我们网购一张大的床,商家在发给我们的时候会直接是一张床吗,那肯定是将床拆开,这样的话也便于运输存储,这一过程和我们的序列化是一样的,当我们收到货的时候,再将床进行一个组装使用,这就相当于反序列化。

对象:

行动或思考时作为目标的事物,我们在学习C的时候应该听说万物皆对象这句话,也就是说一切的事物都可以叫做对象,搞对象,有的有两个对象,有的有三个,有的一个没有等等,这都叫做对象。

类:

实现某种功能的集合,这个概念就比较抽象了,不过我们经常可以听到物以类聚,人以群分,比如我们是人类,这个都属于是类。

php序列化的函数:
serialize()    //将一个对象转换成一个字符串
unserialize() //将字符串还原成一个对象

二、分类

php反序列化,我们从两个方面进行讲解,分别是无类和有类。

1、无类

我们来用一个CTF的题目进行讲解(放在了本地)

代码如下
<?php
error_reporting(0);
include "flag.php";
$KEY = "nihao";
$str = $_GET['str'];
if (unserialize($str) === "$KEY")
{
echo "$flag";
}
show_source(__FILE__);
?>

我们想要得到flag就得明白这段代码是什么意思?
error_reporting() 是一个PHP函数,用于设置脚本的错误报告级别。它控制PHP引擎将哪些类型的错误或警告显示给用户。我们的代码中设置的级别是0,也就是说禁用所有错误报告,即不显示任何错误、警告或提示信息。
接下来是包含了一个flag.php文件,以我们的一个经验来说,在这个文件中肯定有我们想要的一个flag,再下来是将nihao赋值给变量KEY,使用GET传参,传的是str,将传进来的参数赋值给变量str,下面是一个判断语句:如果我们传进的参数的反序列化===$KEY,则输出flag。
show_source(__ FILE__ )表示的是将该文件的源代码输出到浏览器上面。show_source()表示的高亮显示文件中的内容,比如我们想要显示C盘下的1.php文件,则表示为show_source('c://1.php');__FILE__ 是一个魔术常量,表示当前文件的完整路径和文件名。

注:

在代码中=表示的是赋值
==表示的是等于,转换成相同的类型,进行比较,比如:'123'==123返回的是ture。
===表示的是恒等于,它会将内容和类型都进行比较,比如:'123'===123返回的是false。

读懂以上代码的话,这个题就非常简单了,我们传进去的str的反序列化等于nihao,就可以实现输出flag,反着推一下,就是将nihao进行序列化一下,传进去,代码会将他反序列化正好又回到了nihao,这样就可以实现输出flag。将nihao序列化一下

传进去,看结果

可以看到确实是显示出来了flag.

flag.php代码如下:
 <?php $flag='you are very cool'; ?>

这是一个字符型的,我们将他改成整型的看一下效果

代码如下
  <?php
error_reporting(0);
include "flag.php";
$KEY = 123;
$str = $_GET['str'];
if (unserialize($str) === $KEY)
{
echo "$flag";
}
show_source(__FILE__);
?>

依据字符型的一个传参,我们可以猜到应该传:i:3:"123",但是并不是,我们应该用php在线运行工具看一下123的序列化是什么?

将其上传

得到他的Flag。

2、有类

判断它有没有类,只需要看他有没有class,我们用以下代码进行一个讲解

代码如下:
<?php 
show_source(__FILE__);
class nihao{
var $test='123';
}
$A = new nihao;
$B = serialize($A);
var_dump($B);
?>

解释一下这段代码,代码中定义了一个名为 nihao 的类(类名的命名规则通常建议遵循驼峰命名法,首字母小写),类中有一个名为 test 的属性,其值为 '123'。

接下来,通过创建一个 nihao 类的实例并赋值给变量变量A。然后,使用 serialize() 函数将该实例序列化为一个字符串,并将结果赋值给变量变量B.
最后,使用 var_dump() 打印出变量B 的值,以便查看该序列化字符串的结果。
看一下输出结果

结果为:string(37) "O:5:"nihao":1:{s:4:"test";s:3:"123";}" 当前的是个对象,调用什么nihao类 只有1个值,来看一下两个值的

代码如下:
<?php 
show_source(__FILE__);
class nihao{
var $test='123';
var $C="ccc";
}
$A = new nihao;
$B = serialize($A);
var_dump($B);
?>

这是一个简单的类的讲解,可能看到这里你还是会很迷惑,这个序列化也好,反序列化也好,不就是个正常功能吗,这哪里会有漏洞产生呢?接下来说一下漏洞原理。

漏洞原理:

未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,sql注入,目录遍历等不可控后果,在反序列化的过程中自动触发了某些魔术方法。当进行反序列化的时候就有可能会触发对象中的一些魔术方法。

在原理中提到了一个名词:魔术方法。魔术方法是在 PHP 类中定义的特殊方法,以双下划线开头和结束,例如 __construct(),__toString(),__get() 等。这些方法会在特定的情况下自动调用,而无需手动调用。魔术方法使得我们可以对类的行为进行自定义,并实现一些特殊的功能,如对象的初始化、对象的字符串表示、属性的访问控制等。通过定义魔术方法,我们可以在特定的时候干预类的行为和处理。

以下是我们的一些魔术方法:

__construct()//创建对象时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当脚本尝试将对象调用为函数时触发

搞三个简单的说下:__construct()、__destruct()、__wakeup()

代码如下:
<?php
class nihao{
var $test=123;
function __construct()
{
echo '我是构造函数';
}

}
$a =new nihao;
?>

这个函数在new的时候会自行调用

代码如下:
<?php
class nihao {
var $test = 123;
function __destruct() {
echo '我是析构函数';
}

}
$a = new nihao;
?>

这个函数在代码执行完毕的时候会自行调用

代码如下:
<?php
class nihao{
var $test;
function __wakeup(){
echo '我是苏醒函数';
}

}

b=serialize(c = unserialize($b);
?>

上面的代码看图片里输入的,__wakeup()函数是在使用反序列化的时候自行调用。

三、打个靶场

网鼎杯2020青龙大赛真题
地址:https://www.ctfhub.com/#/index

由题可知:

由题目命名和函数unserialize可以判断此题目考察的是反序列化知识点。主要是获取flag--存储flag.php 先进行代码分析,看看代码说了什么,看这段代码

这段代码从下往上看,GET接收一个str参数,然后用is_valid进行检测,检测通过就将str进行反序列化,中间一段代码是is_valid的检测内容。最后一段代码执行完成以后,也就是销毁了,在销毁的时候触发了析构函数_destruct.然后析构函数就开始执行。如果op===2,则会强制让他转为1.

isset()函数用于检测变量是否已设置并且非 NULL,符合返回ture,不符合返回false

is_valid()函数检查对象变量是否已经实例化,即实例变量的值是否是个有效的对象。如果指定对象已经创建了对此案实例,那么IsValid()函数返回True,否则返回FALSE。如果参数obejctname的值为NULL,IsValid()函数返回NULL。

接着看下面的代码

传入的op=1就执行write写入,如果为2,就执行读取,因为我们是要flag,所以肯定是要他执行读取。
__destruct函数对this->op进行判断。所以我们要绕过_destruct函数的判断,进入到process函数中的读取。这里使用字符串' 2'(空格2)进行绕过。这样就可以执行读取。

因为最后一步是将str进行反序列化,我们传入的时候就应该是序列化。

传入到str,右键查看源代码。

以上大概就是php反序列化的全部内容,这应该是所有漏洞中最需要代码审计的漏洞,希望能对你有所帮助。如有不足之处,还望指正。

星光安全面向基础!

项目下载

1、公众号后台回复:jboss,获取jboss综合利用工具网盘下载链接。
2、公众号后台回复:
ruoyi,获取ruoyi综合利用工具网盘下载链接。
3、公众号后台回复:
162获取LiqunKit 1.6.2 综合漏洞利用工具网盘下载链接
4、公众号后台回复:
burp,获取burpsuite pro2022.9.1破解版网盘下载链接

文章来源: http://mp.weixin.qq.com/s?__biz=Mzg3MTY3NzUwMQ==&mid=2247487180&idx=1&sn=5924820b24a6d9a433cfbcc5de681875&chksm=cefba59ef98c2c880467284ef34f4bd96c7d44a3fa46417b2a02f2df58c5cad2d11985d27448#rd
如有侵权请联系:admin#unsafe.sh