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

php中open_basedir存在安全隐患

WBOY
WBOYOriginal
2016-05-25 16:41:291090browse

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


教程链接:

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

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn