php中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虚拟主机配置文件,代码如下:
Options -Indexes FollowSymLinks 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;"
里面的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中处理文件路径时没有严格考虑目录的存在,这将导致本地包含或者本地文件读取的绕过。
看一个本地文件任意读取的例子,代码如下:
虽然file是任意提交的,但是限制了前缀必须为img,我们如果想跳出目录读文件,比如,读取网站根目录下的config.php,我们得提交?file=img/../../config.php,但是此处有个限制条件,就是upload目录下不存在img文件夹,在windows文件系统里,系统不会去考虑目录存在不存在,会直接跳转目录从而导致漏洞;但linux文件系统非常严谨,它会仔细判断每一层目录是否存在,比如这里由于不存在img,则跳出去读取文件的时候直接报错.
再看一个类似的本地包含的例子,代码如下:
由于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/".
教程链接:
随意转载~但请保留教程地址★
下一篇: 怎么将print_r()的输出存入变量