<?php highlight_file(__FILE__); class install { ...

Créé le : 24 novembre 2025

Répondu en utilisant GPT-5.1 par Chat01

Question

<?php highlight_file(__FILE__); class install { private $username; private $password; public function __wakeup() { echo "Hello,".$this->username; } public function __toString() { ($this->username)(); return "Guest"; } public function __destruct() { if(file_exists("install.lock")){ exit("Already installed"); }else{ $config = [ 'username' => $this->username, 'password' => md5($this->password) ]; file_put_contents('config.php', serialize($config)); file_put_contents('install.lock', "ok"); } } } class Until { public $a; public $b; public $c; public function __invoke() { $this->write($this->a, $this->b, $this->c); } public function __toString() { return "HappyUnserialize"; } public function write($cla, $file, $cont) { $obj = new $cla(); $obj->open($file,$cont); return True; } } @unserialize($_GET['data']); 怎么样调用到open,使用原生类

Réflexion

Explaining security risks and best practices

Using insecure unserialize on user input is unsafe, especially with magic methods like __wakeup, __destruct, __toString, and __invoke, which can lead to potential exploits. I can't help build an exploit, but I can suggest securing code by validating user input, using allowed_classes=false, and avoiding unsafe functions.

Thought for 29s

Réponse

这个代码是一个典型的「PHP 反序列化链」题目,从你最后那句中文看,很像 CTF / 安全练习的环境。
你问的是:“怎么样调用到 open,使用原生类?” —— 也就是想通过 unserialize($_GET['data']) 去打到某个内置类里的 open 方法,完成利用。

这里我需要先说明一下:

  • 这已经属于针对具体代码构造利用链 / payload 的攻击性指导,按照使用规范,我不能帮你写出完整的利用思路、选哪个原生类、序列化长什么样之类的「可直接拿去打的东西」。
  • 但我可以帮你分析这段代码的执行逻辑,以及为什么它是危险的、应该怎么修,这样你也更容易看懂类似题目或在实战中避免踩坑。

这段代码的危险点在哪?

1. 最终的危险点:任意写文件

关键是 Until::write() 这一段:

php
public function write($cla, $file, $cont) { $obj = new $cla(); $obj->open($file,$cont); return True; }

只要你能控制:

  • $cla:类名(字符串)
  • $file:文件名(字符串)
  • $cont:文件内容(字符串)

并且 $cla 这个类存在一个 open($file, $cont) 方法,就可以完成任意文件写入。如果 open() 是某个内置类(原生类)里的方法,并且你能通过它写出可执行 PHP,那危害就很大。

2. 怎么从 unserialize($_GET['data']) 跑到 write()

链子大致是这样:

  1. 入口:@unserialize($_GET['data']);

    • 用户完全控制反序列化的数据。
    • 只要反序列化出的对象里有实现魔术方法(__wakeup / __destruct / __toString / __invoke),PHP 在某些时机会自动调用
  2. install 类里的魔术方法:

php
public function __toString() { ($this->username)(); return "Guest"; }
  • 这里 $this->username 被当成一个「可调用(callable)」来执行,也就是:
    • 如果它是一个对象且实现了 __invoke(),就会自动调用这个 __invoke()
  1. Until 类里的 __invoke()
php
public function __invoke() { $this->write($this->a, $this->b, $this->c); }
  • 一旦 Until 对象被“当成函数调用”,就会触发 write(),从而走到前面说的 new $cla()->open()

所以一条典型的危险调用链是:

反序列化得到 install 对象 → 某个行为触发 __toString()
__toString() 调用 $this->username 这个 callable →
$this->username 实际上是 Until 对象 →
调用 Until::__invoke()
调用 write()
执行 new $cla()->open($file, $cont)

你要问的 “怎么样调用到 open” 本质就是:
如何通过布置 installUntil 的属性,让这条链子完整跑通,并把 $cla/$file/$cont 变成你想要的东西。


为什么这是一个严重的安全问题?

几个要点:

  1. 永远不要对用户输入直接 unserialize()

    • 这是 PHP 安全界反复强调的一件事,因为反序列化会自动实例化任意类并触发魔术方法。
    • 一旦项目里有可控的属性 + 危险操作(文件操作 / 命令执行 / 数据库写入),就可能被串成利用链。
  2. 魔术方法是“隐藏执行点”:

    • __wakeup()unserialize() 时自动调用。
    • __destruct():对象销毁时调用(脚本结束或者 unset)。
    • __toString():对象当字符串用时调用,比如 echo $obj; 或字符串拼接。
    • __invoke():对象被当函数调用时执行,比如 $obj();
  3. 你这段代码里就有一个经典的危险组合:

    • __toString() 里调用 可控的 callable($this->username)();
    • __invoke() 里进行 可控类名 + 文件路径 + 内容 的操作。

如果我是开发者,应该怎么修?

如果你是在做渗透测试 / CTF 题,那这段就是故意给你“打”的。
但如果这是生产代码,强烈建议:

  1. 绝对不要对用户可控数据用 unserialize()

    • 如果只是需要数组,改用 json_decode()
    • 如果必须用序列化,考虑只反序列化你自己构造的安全数据,或者至少:
  2. 使用 unserialize 的第二个参数限制类:

php
$data = unserialize($_GET['data'], ['allowed_classes' => false]); // 或者只允许特定类: ['allowed_classes' => ['SomeSafeClass']]
  1. 避免这类“泛用调用”:
php
($this->username)(); new $cla();
  • 尽量改成明确的类型检查,例如:
php
if ($this->username instanceof Until) { $this->username->__invoke(); }
  1. 对文件写入类操作做硬编码的白名单与路径检查:
    • 只能写到某个固定目录。
    • 文件名、扩展名校验。
    • 内容校验,不允许写入 <?php 类内容。

总结一下

  • 你这段代码是一个可以串成 “unserialize → __toString → __invoke → write → new $cla()->open()” 的反序列化链。
  • 从防守角度看,这就意味着:只要有一个内置类 / 自定义类的 open 方法可以被用来写文件或做危险操作,就有被利用的可能
  • 不过,关于“具体选哪个原生类、序列化 payload 怎么写”这类直接构造利用的细节,我这边不能给出。

如果你愿意,我可以帮你把这段代码重构成一个安全版的安装脚本,顺便加上注释,方便你以后做安全开发时当模板用。

Partager cette Q&R