在php中open_basedir是php中一个用得不多的函数,但是open_basedir函数一不小心就给人家给进入你服务器了,open_basedir到底有多神奇我们来看看吧.
先看一段我们不考虑open_basedir安全问题代码:
在php写了句require_once ‘../Zend/Loader.php’;报错:
- Warning: require_once() [function.require-once]: open_basedir restriction in effect. File(../Zend/Loader.php) is not within the allowed path(s): (D:/phpnow/vhosts/zf.com;C:/Windows/Temp;) in D:/phpnow/vhosts/zf.com/index.php on line 6
-
- Warning: require_once(../Zend/Loader.php) [function.require-once]: failed to open stream: Operation not permitted in D:/phpnow/vhosts/zf.com/index.php on line 6
-
- Fatal error: require_once() [function.require]: Failed opening required '../Zend/Loader.php' (include_path='D:/phpnow/vhosts/zf.comZend;.;C:/php5/pear') in D:/phpnow/vhosts/zf.com/index.php on line 6
字面分析是受到了open_basedir的限制,造成Operation not permitted(操作不被允许).
打开php.ini跳转到open_basedir相关设置段落:
- ; open_basedir, if set, limits all file operations to the defined directory
- ; and below. This directive makes most sense if used in a per-directory
- ; or per-virtualhost web server configuration file. This directive is
- ; *NOT* affected by whether Safe Mode is turned On or Off.
;open_basedir =如果设置了open_basedir,那么所有能被操作的文件就只能限制在open_basedir指定的目录里面,这个在虚拟主机里面这个指令相当有用,不管安全模式是否打开,这个指令都不受影响,看来php.ini没有设置open_basedir,打开apache虚拟主机配置文件,代码如下:
- <virtualhost *>
- <directory "../vhosts/phpfensi.com">
- Options -Indexes FollowSymLinks
- </directory>
- ServerAdmin admin@phpfensi.com
- DocumentRoot "../vhosts/phpfensi.com"
- ServerName zf.com:80
- ServerAlias *.zf.com
- ErrorLog logs/zf.com-error_log
- php_admin_value open_basedir "D:/phpnow/vhosts/zf.com;C:/Windows/Temp;"
- </virtualhost>
里面的php_admin_value open_basedir就限定了操作目录,我这里是本地测试,安全因素不考虑,直接将 php_admin_value open_basedir “D:/phpnow/vhosts/zf.com;C:/Windows/Temp;” 删除掉,重新启动apache.
上面如果给利用完可以可随意删除服务器文件了,但是比较幸运的是目前php站点的安全配置基本是open_basedir+safemode,确实很无敌、很安全,即使在权限没有很好设置的环境中,这样配置都是相当安全的,当然了,不考虑某些可以绕过的情况。本文讨论两点开启open_basedir后可能导致的安全隐患(现实遇到的),一个也许属于php的一个小bug,另外一个可能是由于配置不当产生的。
一、open_basedir中处理文件路径时没有严格考虑目录的存在,这将导致本地包含或者本地文件读取的绕过。
看一个本地文件任意读取的例子,代码如下:
- <?php
- $file = $_GET['file'];
- preg_match("/^img/", $file) or die('error_file');
- $file='/home/www/upload/'.$file;
- file_exists($file) or die('no_such_file');
- $f = fopen("$file", 'r');
- $jpeg = fread($f, filesize("$file"));
- fclose($f);
- Header("Content-type: image/jpeg");
- Header("Content-disposition: inline; filename=test.jpg");
- echo $jpeg;
- ?>
虽然file是任意提交的,但是限制了前缀必须为img,我们如果想跳出目录读文件,比如,读取网站根目录下的config.php,我们得提交?file=img/../../config.php,但是此处有个限制条件,就是upload目录下不存在img文件夹,在windows文件系统里,系统不会去考虑目录存在不存在,会直接跳转目录从而导致漏洞;但linux文件系统非常严谨,它会仔细判断每一层目录是否存在,比如这里由于不存在img,则跳出去读取文件的时候直接报错.
再看一个类似的本地包含的例子,代码如下:
<?php
include "aaa".$_GET['lang'].".php";
?>
由于linux文件系统的限制,我们无法利用旁注去包含tmp下的文件.
linux严谨的考虑在php那里显然没有得到深刻的体会,在开启了open_basedir的时候,php对传入的文件路径进行了取真实路径的处理,然后跟open_basedir中设置的路径进行比较,代码如下:
- ……
- /* normalize and expand path */
- if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) {
- return -1;
- }
-
- path_len = strlen(resolved_name);
- memcpy(path_tmp, resolved_name, path_len + 1); /* safe */
- ……
但php在处理的时候忽略了检查路径是否存在,于是在开启了open_basedir时,上面那个文件读取的例子,我们可以使用?file=img/../../config.php来直接读取了,此时提交的路径已经被处理成/home/www/config.php了,所以不存在任何读取问题了.
问题由渗透测试的时候遇到绕过的情况从而导致疑问,经分析环境差异,然后xi4oyu牛指点有可能是open_basedir的问题后测试总结出这是php的一个小的bug,但是很有可能导致安全隐患.
二、open_basedir的值配置不当,有可能导致目录跨越.
很多管理员都知道设置open_basedir,但在配置不当的时候可能发生目录跨越的问题,错误的配置:/tmp:/home/www,正确的配置:/tmp/:/home/www/,代码如下:
- ……
- /* Resolve open_basedir to resolved_basedir */
- if (expand_filepath(local_open_basedir, resolved_basedir TSRMLS_CC) != NULL) {
- /* Handler for basedirs that end with a / */
- resolved_basedir_len = strlen(resolved_basedir);
- if (basedir[strlen(basedir) - 1] == PHP_DIR_SEPARATOR) {
- if (resolved_basedir[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) {
- resolved_basedir[resolved_basedir_len] = PHP_DIR_SEPARATOR;
- resolved_basedir[++resolved_basedir_len] = '/0';
- }
- } else {
- resolved_basedir[resolved_basedir_len++] = PHP_DIR_SEPARATOR;
- resolved_basedir[resolved_basedir_len] = '/0';
- }
- ……
php考虑了以/结束的路径,但是如果没有/,就直接带入下文比较了.
于是,当新建一个网站为 /home/wwwoldjun/(均已经分别设置open_basedir),如果配置错误,则可以从/home/www/目录跳转到/home/wwwoldjun/目录.
举个渗透实例,某idc商在租用虚拟主机的时候如此分配空间/home/wwwroot/userxxx/、/home/wwwroot/useryyy/...,而open_basedir是这样错误配置的:/tmp:/home/wwwroot/userxxx、/tmp:/home/wwwroot/useryyy,如果我们想通过配置的错误轻易渗透下userxxx站点,我们该怎么做?
特殊值,指明脚本的工作目录将被作为基准目录,但这有些危险,因为脚本的工作目录可以轻易被 chdir() 而改变.
在 httpd.conf 文件中中,open_basedir 可以像其它任何配置选项一样用“php_admin_value open_basedir none”的方法关闭(例如某些虚拟主机中)。
在 Windows 中,用分号分隔目录。在任何其它系统中用冒号分隔目录,作为 Apache 模块时,父目录中的 open_basedir 路径自动被继承.
用 open_basedir 指定的限制实际上是前缀,不是目录名,也就是说“open_basedir = /dir/incl”也会允许访问“/dir/include”和“/dir/incls”,如果它们存在的话。如果要将访问限制在仅为指定的目录,用斜线结束路径名,例如:“open_basedir = /dir/incl/”. |