当前位置:首页 > PHP教程 > php应用 > 列表

php中open_basedir存在安全隐患

发布:smiling 来源: PHP粉丝网  添加日期:2014-08-22 10:26:57 浏览: 评论:0 

在php中open_basedir是php中一个用得不多的函数,但是open_basedir函数一不小心就给人家给进入你服务器了,open_basedir到底有多神奇我们来看看吧.

先看一段我们不考虑open_basedir安全问题代码:

在php写了句require_once ‘../Zend/Loader.php’;报错:

  1. 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 
  2.  
  3. 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 
  4.  
  5. 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相关设置段落:

  1. ; open_basedir, if set, limits all file operations to the defined directory 
  2. ; and below.  This directive makes most sense if used in a per-directory 
  3. ; or per-virtualhost web server configuration file. This directive is 
  4. ; *NOT* affected by whether Safe Mode is turned On or Off. 

;open_basedir =如果设置了open_basedir,那么所有能被操作的文件就只能限制在open_basedir指定的目录里面,这个在虚拟主机里面这个指令相当有用,不管安全模式是否打开,这个指令都不受影响,看来php.ini没有设置open_basedir,打开apache虚拟主机配置文件,代码如下:

  1. <virtualhost *> 
  2.     <directory "../vhosts/phpfensi.com"> 
  3.         Options -Indexes FollowSymLinks 
  4.     </directory> 
  5.     ServerAdmin admin@phpfensi.com 
  6.     DocumentRoot "../vhosts/phpfensi.com" 
  7.     ServerName zf.com:80 
  8.     ServerAlias *.zf.com 
  9.     ErrorLog logs/zf.com-error_log 
  10.     php_admin_value open_basedir "D:/phpnow/vhosts/zf.com;C:/Windows/Temp;" 
  11. </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中处理文件路径时没有严格考虑目录的存在,这将导致本地包含或者本地文件读取的绕过。

看一个本地文件任意读取的例子,代码如下:

  1. <?php 
  2. $file = $_GET['file']; 
  3. preg_match("/^img/"$fileor die('error_file'); 
  4. $file='/home/www/upload/'.$file
  5. file_exists($fileor die('no_such_file'); 
  6. $f = fopen("$file"'r'); 
  7. $jpeg = fread($ffilesize("$file")); 
  8. fclose($f); 
  9. Header("Content-type: image/jpeg"); 
  10. Header("Content-disposition: inline; filename=test.jpg"); 
  11. echo $jpeg
  12. ?> 

虽然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中设置的路径进行比较,代码如下:

  1. …… 
  2. /* normalize and expand path */ 
  3. if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) { 
  4. return -1; 
  5.  
  6. path_len = strlen(resolved_name); 
  7. memcpy(path_tmp, resolved_name, path_len + 1); /* safe */ 
  8. …… 

但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/,代码如下:

  1. …… 
  2. /* Resolve open_basedir to resolved_basedir */ 
  3. if (expand_filepath(local_open_basedir, resolved_basedir TSRMLS_CC) != NULL) { 
  4. /* Handler for basedirs that end with a / */ 
  5. resolved_basedir_len = strlen(resolved_basedir); 
  6. if (basedir[strlen(basedir) - 1] == PHP_DIR_SEPARATOR) { 
  7. if (resolved_basedir[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) { 
  8. resolved_basedir[resolved_basedir_len] = PHP_DIR_SEPARATOR; 
  9. resolved_basedir[++resolved_basedir_len] = '/0'; 
  10. } else { 
  11. resolved_basedir[resolved_basedir_len++] = PHP_DIR_SEPARATOR; 
  12. resolved_basedir[resolved_basedir_len] = '/0'; 
  13. …… 

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/”.

Tags: open_basedir php安全隐患

分享到: