【问题标题】:Can an appended file suffix to a parameter for file_get_contents be bypassed?可以绕过 file_get_contents 参数的附加文件后缀吗?
【发布时间】:2012-10-04 16:21:33
【问题描述】:

考虑下面的代码

<?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);

我们都知道(至少我希望如此)预先设置绝对路径绝不是防止离开给定路径的充分方法 - 只需在查询字符串中插入一个或多个“../”即可到达文件系统上的任何路径。

我的问题是:与给定的示例类似,$_GET['filename'] 是否也可以通过绕过给定扩展后缀的方式进行操作,即 .txt 以外的文件是回响?我特别想到某些控制字符绕过附加的文件扩展名,就像../ 处理前缀一样。

我尝试在查询字符串中添加一些控制字符(例如用于删除的 ASCII 代码 127)或使用 &amp;&amp;|&gt; 连接两个文件名,但都无济于事,我想知道是否存在完全有这种可能。

(顺便说一句,我想补充一点,这个问题并不是为了利用系统而提出的。这纯粹是我最近想到的一个假设性问题。)

【问题讨论】:

    标签: php security code-injection


    【解决方案1】:

    当然,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(),它将确定真正被访问的完整路径。 所以,/absolute/path/../../../../etc/passwd\0.txt,传递给realpath() 将返回/etc/passwd。然后,您可以对返回的值进行进一步验证(例如,扩展名是否仍为 .txt 或者是否在指定/必需的目录中)。

    【讨论】:

    • 感谢您的全面回答,我没有想到空字节。但是,我尝试使用 $filename = "testfile\0" 并将文件放在指定路径但 file_get_contents 返回 false(Ubuntu 11.04,PHP 5.3.5)。这个环境依赖吗?
    • @moeso 我不知道;我用您的代码和$filename = "../../../../../etc/passwd\0"; 的复制+粘贴进行了测试,它运行良好(打印/etc/passwd)。我找不到任何说明它依赖于系统/版本的文档 - 但我总是有可能错过了一些东西。
    • 跟进:只是为了好玩,我尝试了与上面评论中描述的相同的方法,唯一的区别是我使用了古老但新编译的 PHP 版本 4.4.49 和 5.2.0 - 以及 nullbyte你描述的技巧对双方都有效。所以在 5.2.0 和 5.3.5 之间的某个地方,这个洞似乎已经被关闭了。
    • @moeso 哇——这是一个很好的提示。今晚我会亲自测试一下!
    猜你喜欢
    • 2013-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-26
    • 1970-01-01
    • 1970-01-01
    • 2015-09-20
    • 2021-03-07
    相关资源
    最近更新 更多