Maison  >  Article  >  php教程  >  php中open_basedir存在安全隐患

php中open_basedir存在安全隐患

WBOY
WBOYoriginal
2016-05-25 16:41:291092parcourir

在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/phprm.com"> 
Options -Indexes FollowSymLinks 
</directory> 
ServerAdmin admin@phprm.com 
DocumentRoot "../vhosts/phprm.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[&#39;file&#39;]; 
preg_match("/^img/", $file) or die(&#39;error_file&#39;); 
$file=&#39;/home/www/upload/&#39;.$file; 
file_exists($file) or die(&#39;no_such_file&#39;); 
$f = fopen("$file", &#39;r&#39;); 
$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[&#39;lang&#39;].".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] = &#39;/0&#39;; 
} 
} else { 
resolved_basedir[resolved_basedir_len++] = PHP_DIR_SEPARATOR; 
resolved_basedir[resolved_basedir_len] = &#39;/0&#39;; 
} 
……

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


教程链接:

随意转载~但请保留教程地址★

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn