Home > Article > Backend Development > A brief analysis of the security risks of open_basedir in PHP_PHP Tutorial
In php, open_basedir is a rarely used function in php, but the open_basedir function may accidentally allow someone to access your server. Let’s take a look at how magical open_basedir is.
Let’s first look at a piece of code where we don’t consider open_basedir security issues
I wrote require_once ‘../Zend/Loader.php’ in php; Error:
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 Literal analysis is restricted by open_basedir, resulting in Operation not permitted.
Open php.ini and jump to the relevant settings section of 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 =If open_basedir is set, all files that can be operated can only be limited to the directory specified by open_basedir. This command is quite useful in a virtual host. This command is not affected whether safe mode is turned on or not. It seems that php.ini does not set open_basedir. Open the apache virtual host configuration file:
The code is as follows | Copy code | ||||||||
ServerAdmin admin@zf.com DocumentRoot "../vhosts/zf.com" ServerName zf.com:80ServerAlias *.zf.com ErrorLog logs/zf.com-error_log
|
The code is as follows | Copy code |
$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;<🎜> ?> |
Although the file can be submitted arbitrarily, the prefix is restricted to img. If we want to read the file out of the directory, for example, to read config.php in the root directory of the website, we have to submit?file=img/../ ../config.php, but there is a restriction here, that is, the img folder does not exist in the upload directory. In the Windows file system, the system will not consider whether the directory exists, and will directly jump to the directory, causing a vulnerability; However, the Linux file system is very strict. It will carefully determine whether each directory exists. For example, since img does not exist here, it will directly report an error when it jumps out to read the file. Look at the following diagram:
Let’s look at a similar example of local inclusion:
The code is as follows | Copy code |
代码如下 | 复制代码 |
include "aaa".$_GET['lang'].".php"; ?> |
?>
Due to the limitations of the Linux file system, we cannot use side comments to include files under tmp.
代码如下 | 复制代码 |
…… path_len = strlen(resolved_name); |
The code is as follows | Copy code | ||||
…… /* 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 */ ……
|
The code is as follows | Copy code |
…… /* 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 considers the path ending with /, but if there is no /, it will be directly brought into the following comparison.
So, when a new website is /home/wwwoldjun/ (open_basedir has been set separately), if the configuration is wrong, you can jump from the /home/www/ directory to the /home/wwwoldjun/ directory.
As an example of penetration, an IDC provider allocates space /home/wwwroot/userxxx/, /home/wwwroot/useryyy/... when renting a virtual host, and open_basedir is misconfigured like this: /tmp: /home/wwwroot/userxxx, /tmp:/home/wwwroot/useryyy. If we want to easily penetrate the userxxx site through configuration errors, what should we do?
The special value . specifies that the script's working directory will be used as the base directory. But this is somewhat dangerous, because the script's working directory can be easily changed by chdir().
In the httpd.conf file, open_basedir can be turned off like any other configuration option using the "php_admin_value open_basedir none" method (such as in some virtual hosts).
In Windows, separate directories with semicolons. Use colons to separate directories on any other system. As an Apache module, the open_basedir path in the parent directory is automatically inherited.
The restrictions specified with open_basedir are actually prefixes, not directory names. That is to say "open_basedir = /dir/incl" will also allow access to "/dir/include" and "/dir/incls" if they exist. If you want to restrict access to only the specified directory, end the pathname with a slash. For example: "open_basedir = /dir/incl/".