考虑以下代码
<?php
// warning: this code is unsafe and for demonstrational purposes only,
// do not use in a production environment
$filename = $_GET['filename'];
$extension = 'txt';
$path = '/absolute/path';
$fullFilename = sprintf('%s/%s.%s', $path, $filename, $extension);
echo file_get_contents($fullFilename);
我们都知道(至少我希望如此),前置绝对路径绝不是防止离开给定路径的充分手段 - 人们可以简单地插入一个或多个“../
"s 到查询字符串以到达文件系统上的任何路径。
我的问题是:与给定的示例类似,$_GET['filename']
是否也可以以绕过给定扩展名后缀的方式进行操作,即回显 .txt 以外的文件?我特别想到某些控制字符会绕过附加的文件扩展名,其方式与 ../
使用前缀的方式相同。
我尝试在查询字符串中添加一些控制字符(例如用于删除的 ASCII 代码 127),或使用 &&
、|
或 >< 连接两个文件名
,但一切都无济于事,我想知道是否存在这种可能性。
(顺便说一句,我想补充一点,提出这个问题并不是为了利用系统。这纯粹是我最近想到的一个假设问题。)
最佳答案
当然,nullbyte
、\0
(如果您想在查询字符串中测试,则为 %00
)将具有 file_get_contents()
当字符串命中时停止处理它。
因此,如果 $_GET['filename']
是 ../../../../../etc/passwd%00
(例如如 your_page.php?filename=../../../../etc/passwd%00
),file_get_contents()
永远不会看到结尾 .txt
(它会看到它,但不会处理它)。
遗憾的是,sprintf
在构建字符串时不会去除空字节。我不确定其他控制字符,但如果有的话 - 我总是建议删除空字节。不过,更直观的方法是构建允许字符的白名单,并对输入执行 preg_match()
。
再进一步,您可以使用 PHP 的 realpath()
在构建的字符串上,它将确定真正被访问的完整路径。
因此,传递给 realpath()
的 /absolute/path/../../../../etc/passwd\0.txt
将返回 /etc/passwd
.然后,您可以对返回的值进行进一步验证(例如,扩展名是否仍为 .txt
或者是否位于指定/所需的目录中)。
关于php - 是否可以绕过 file_get_contents 参数的附加文件后缀?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12731547/