热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PHP绕过open_basedir列目录的研究

首发drops:drops.wooyun.orgtips3978。近期由于在开发自己的webshell,所以对PHP一些已有的漏洞进行了一定的研究,并且也自己发现了部分PHP存在的安全隐患。这篇文章我来与大家分享一下自己对于PHP中open_basedir绕过并列举目录的方法总结。0x0

首发drops:http://drops.wooyun.org/tips/3978 。 近期由于在开发自己的webshell,所以对PHP一些已有的漏洞进行了一定的研究,并且也自己发现了部分PHP存在的安全隐患。这篇文章我来与大家分享一下自己对于PHP中open_basedir绕过并列举目录的方法总结。 0x0

首发drops:http://drops.wooyun.org/tips/3978 。

近期由于在开发自己的webshell,所以对PHP一些已有的漏洞进行了一定的研究,并且也自己发现了部分PHP存在的安全隐患。这篇文章我来与大家分享一下自己对于PHP中open_basedir绕过并列举目录的方法总结。


0x01 open_basedir的简介

Open_basedir是PHP设置中为了防御PHP跨目录进行文件(目录)读写的方法,所有PHP中有关文件读、写的函数都会经过open_basedir的检查。Open_basedir实际上是一些目录的集合,在定义了open_basedir以后,php可以读写的文件、目录都将被限制在这些目录中。

设置open_basedir的方法,在linux下,不同的目录由“:”分割,如“/var/www/:/tmp/”;在Windows下不同目录由“;”分割,如“c:/www;c:/windows/temp”。

在现在这个各种云、虚拟主机横行的时期,人们希望open_basedir作为一个横亘在不同用户之间的屏障,有力地保障用户的主机能独立运行,但事实并非人们想象的那么简单。

我们这篇文章着重讲的将是绕过open_basedir进行目录的列举与遍历,为何我们不说具体文件的读、写,因为文件读写的洞是危害比较大的漏洞了,在php5.3以后很少有能够绕过open_basedir读写文件的方法。

0x02 利用DirectoryIterator + Glob 直接列举目录

这是@/fd 脚本(http://zone.wooyun.org/content/11268)里给出的第一个方法。

DirectoryIterator 是php5中增加的一个类,为用户提供一个简单的查看目录的接口(The DirectoryIterator class provides a simple interface for viewing the contents of filesystem directories)。

glob: 数据流包装器是从 PHP 5.3.0 起开始有效的,用来查找匹配的文件路径。

结合这两个方式,我们就可以在php5.3以后对目录进行列举。在实测中,我们得知,此方法在Linux下列举目录居然可以无视open_basedir。

实例代码:
open_basedir : %s 
', ini_get('open_basedir')); $file_list = array(); // normal files $it = new DirectoryIterator("glob:///*"); foreach($it as $f) { $file_list[] = $f->__toString(); } // special files (starting with a dot(.)) $it = new DirectoryIterator("glob:///.*"); foreach($it as $f) { $file_list[] = $f->__toString(); } sort($file_list); foreach($file_list as $f){ echo "{$f}
"; } ?>

执行我们可以发现,open_basedir为/usr/share/nginx/www/:/tmp/,但我们成功列举了/根目录下的所有文件:

PHP绕过open_basedir列目录的研究1497.png

这个方法也是迄今为止最方便的方法,他不用暴力猜解目录,而是直接列举。但他对php版本、系统版本有一定要求,在5.3以上可列举(5.5/5.6可能会有修复?在官方没看到有fix),需要在Linux下才能绕过open_basedir。

0x03 realpath列举目录

这是@/fd 脚本(http://zone.wooyun.org/content/11268)里给出的第二个方法。

Realpath函数是php中将一个路径规范化成为绝对路径的方法,它可以去掉多余的../或./等跳转字符,能将相对路径转换成绝对路径。

在开启了open_basedir以后,这个函数有个特点:当我们传入的路径是一个不存在的文件(目录)时,它将返回false;当我们传入一个不在open_basedir里的文件(目录)时,他将抛出错误(File is not within the allowed path(s))。

所以我们可以通过这个特点,来进行目录的猜解。举个例子,我们需要猜解根目录(不在open_basedir中)下的所有文件,只用写一个捕捉php错误的函数err_handle()。当猜解某个存在的文件时,会因抛出错误而进入err_handle(),当猜解某个不存在的文件时,将不会进入err_handle()。

那么由此我们来算算效率。假如一个文件名长度为6位(如config、passwd等全小写不带数字)的文件,我们最差需要枚举多少次才能猜测到他是否存在:
26 ** 6 = 308915776次


PHP绕过open_basedir列目录的研究2150.png

这样是需要跑很久的,基本每次跑的时候我都没耐心了,这样暴力猜解肯定是不行的。那么,有什么好办法可以变这个“鸡肋”的漏洞为一个“好用”的漏洞?

熟悉Windows + PHP的同学应该还记得Windows下有两个特殊的通配符:<、>

对,我们这里就借用这些通配符的力量来列举目录。写个简单的POC来列举一下:
open_basedir: %s
", ini_get('open_basedir')); set_error_handler('isexists'); $dir = 'd:/test/'; $file = ''; $chars = 'abcdefghijklmnopqrstuvwxyz0123456789_'; for ($i=0; $i <'; realpath($file); } function isexists($errno, $errstr) { $regexp = '/File\((.*)\) is not within/'; preg_match($regexp, $errstr, $matches); if (isset($matches[1])) { printf("%s
", $matches[1]); } } ?>

首先设置open_basedir为当前目录,并枚举d:/test/目录下的所有文件。将错误处理交给isexists函数,在isexists函数中匹配出目录名称,并打印出来。

执行可以看到:


PHP绕过open_basedir列目录的研究2934.png

Open_basedir为c:\wamp\www,但我们列举出了d:/test/目录下的文件。

当然,这是个很粗糙的POC,因为我并没有考虑到首字母相同的文件,所以这个POC只能列举首字母不同的文件。

如果首字母相同,我们只需要再枚举第二个字符、第三个字符依次类推,即可列举出目录中所有文件。

这个方法好处是windows下php所有版本通用,当然坏处就是只有windows下才能使用通配符,如果是linux下就只能暴力猜解了。

0x04 SplFileInfo::getRealPath列举目录

受到上一个方法的启发,我开始在php中寻找类似的方法。一旦realpath不能使用的情况下,也能找到替代方式。

我找到了新方法:http://www.wooyun.org/bugs/wooyun-2010-083453 ,使用的方式是SplFileInfo::getRealPath。

SplFileInfo类是PHP5.1.2之后引入的一个类,提供一个对文件进行操作的接口。其中有一个和realpath名字很像的方法叫getRealPath。

这个方法功能和realpath类似,都是获取绝对路径用的。我们在SplFileInfo的构造函数中传入文件相对路径,并且调用getRealPath即可获取文件的绝对路径。

这个方法有个特点:完全没有考虑open_basedir。在传入的路径为一个不存在的路径时,会返回false;在传入的路径为一个存在的路径时,会正常返回绝对路径。

我们的realpath函数还是考虑了open_basedir,只是在报错上没有考虑周全导致我们能够判断某个文件是否存在。但我们可爱的SplFileInfo::getRealPath方法是直接没有考虑open_basedir,就能够判断一个文件是否存在。

那么,我给出一个POC:

open_basedir: %s
", ini_get('open_basedir')); $basedir = 'D:/test/'; $arr = array(); $chars = 'abcdefghijklmnopqrstuvwxyz0123456789'; for ($i=0; $i <'); $re = $info->getRealPath(); if ($re) { dump($re); } } function dump($s){ echo $s . '
'; ob_flush(); flush(); } ?>
只是把之前的POC稍作修改,同样列出了D:/test下的文件:

PHP绕过open_basedir列目录的研究4269.png

这个方法有个特点,不管是否开启open_basedir都是可以枚举任意目录的。而上一个方法(realpath)只有在开启open_basedir且在open_basedir外的时候才会报错,才能列举目录。当然,没有开启open_basedir的时候也不需要这样列举目录了。


0x05 GD库imageftbbox/imagefttext列举目录

GD库一般是PHP必备的扩展库之一,所以我在寻找open_basedir的时候也会看看这些有用的扩展库。

这是新方法:http://www.wooyun.org/bugs/wooyun-2010-083688

我拿imageftbbox举个例子,这个函数第三个参数是字体的路径。我发现当这个参数在open_basedir外的时候,当文件存在,则php会抛出“File(xxxxx) is not within the allowed path(s)”错误。但当文件不存在的时候会抛出“Invalid font filename”错误。

也就是说,我们可以通过抛出错误的具体内容来判断一个文件是否存在。这个方法和realpath有相似性,都会抛出open_basedir的错误。

我也修改了个简单的POC:

open_basedir: %s
", ini_get('open_basedir')); set_error_handler('isexists'); $dir = 'd:/test/'; $file = ''; $chars = 'abcdefghijklmnopqrstuvwxyz0123456789_'; for ($i=0; $i <'; //$m = imagecreatefrompng("zip.png"); //imagefttext($m, 100, 0, 10, 20, 0xffffff, $file, 'aaa'); imageftbbox(100, 100, $file, 'aaa'); } function isexists($errno, $errstr) { global $file; if (stripos($errstr, 'Invalid font filename') === FALSE) { printf("%s
", $file); } } ?>
同样列举一下d:/test

PHP绕过open_basedir列目录的研究5469.png

如上图,我们发现虽然“通配符”在判断是否存在的时候奏效了,但我们真正的文件名并没有显示出来,而是还是以通配符“<><”代替。

所以,这个方法报错的时候并不会把真正的路径爆出来,这也是其与realpath的最大不同之处。所以,我们只能一位一位地猜测,但总体来说,还是能够猜测出来的,只不过可能比realpath更麻烦一些罢了。

0x06 bindtextdomain暴力猜解目录

这是新方法:http://www.wooyun.org/bugs/wooyun-2010-083457

bindtextdomain是php下绑定domain到某个目录的函数。具体这个domain是什么我也没具体用过,只是在一些l10n应用中可能用到的方法(相关函数textdomain、gettext、setlocale,说明:http://php.net/manual/en/function.gettext.php)

Bindtextdomain函数在环境支持Gettext Functions的时候才能使用,而我的windows环境下是没有bindtextdomain函数的,我的linux环境是默认存在这个函数。

PHP绕过open_basedir列目录的研究6098.png

如上图,这个函数第二个参数$directory是一个文件路径。它会在$directory存在的时候返回$directory,不存在则返回false。

写个简单的测试代码:

open_basedir: %s
', ini_get('open_basedir')); $re = bindtextdomain('xxx', $_GET['dir']); var_dump($re); ?>
当/etc/passwd存在的时候输出之:

PHP绕过open_basedir列目录的研究6343.png

当/etc/wooyun不存在的时候返回false:

PHP绕过open_basedir列目录的研究6373.png

并没有考虑到open_basedir。所以,我们也可以通过返回值的不同来猜解、列举某个目录。

但很大的鸡肋点在,windows下默认是没有这个函数的,而在linux下不能使用通配符进行目录的猜解,所以显得很鸡肋。

当然,在万无退路的时候进行暴力猜解目录,也不失为一个还算行的方法。

0x07 总结
open_basedir本来作为php限制跨目录读写文件的最基础的方式,应该需要进行完好的设计。但可能php在当初编写代码的时候并没有进行一个统一的设计,导致每当新增加功能或遇到一些偏僻的函数的时候,都会出现类似“open_basedir绕过”等悲剧。
我曾经写过一篇文章,《lnmp虚拟主机安全配置研究》,中讲述了一个防止虚拟主机跨目录的方法。但受到了一些白帽子的质疑:

PHP绕过open_basedir列目录的研究6713.png

原因是很多人过于相信open_basedir的可靠性。open_basedir固然是一个简单地限制跨目录的方法,但如果过于依赖某一个方法去防御一类攻击,你将会死的很惨。

PHP绕过open_basedir列目录的研究6800.png

open_basedir绕过方法固然有版本局限,但不排除有很多人手中握着0day。像我这样对php造诣并不算高的菜鸟也能找到的open_basedir绕过漏洞,你真的能保证大牛们都没有办法绕过么?

我当然更能相信linux/windows等操作系统自带的权限控制机制,也不会单单相信open_basedir真的能帮我防御什么。

By the way,我上面提到的这些方法,基本都还没有在php的最新版修复(甚至是我自己发现的“0day”),也就是说还真的有这么多通用的方法可以绕过open_basedir。

估计又会有人质疑了,光绕过open_basedir列目录有什么用?

诚然,列目录相比于读、写具体文件,都鸡肋了很多。但很多时候,就是这些看似“鸡肋”的漏洞组合技完成了绝杀。

当列目录可以列出备份文件、整站源码的时候,你还能说列目录是个鸡肋的漏洞么?

安全是一个水桶,不是看哪块木板最高,而是看哪块木板最低。当我们保护住这些“低木板”的时候,才能真正守护住水桶。

而对于渗透测试的同学来说,open_basedir绕过也希望给大家一个新的思路:拿旁站不一定非要提权或弹shell,有时候可能只是简单地列一下目录,就能给你所有。


0x08 参考文档与链接

http://zone.wooyun.org/content/11268

http://www.wooyun.org/bugs/wooyun-2010-083453

http://www.wooyun.org/bugs/wooyun-2010-083688

http://www.wooyun.org/bugs/wooyun-2010-083457

http://php.net/manual/en/class.directoryiterator.php

http://php.net/manual/zh/wrappers.glob.php

http://php.net/manual/en/function.realpath.php

http://php.net/manual/en/splfileinfo.getrealpath.php

http://php.net/manual/en/book.image.php

http://php.net/manual/en/function.gettext.php

推荐阅读
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 目录浏览漏洞与目录遍历漏洞的危害及修复方法
    本文讨论了目录浏览漏洞与目录遍历漏洞的危害,包括网站结构暴露、隐秘文件访问等。同时介绍了检测方法,如使用漏洞扫描器和搜索关键词。最后提供了针对常见中间件的修复方式,包括关闭目录浏览功能。对于保护网站安全具有一定的参考价值。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 本文介绍了在无法联网的情况下,通过下载rpm包离线安装zip和unzip的方法。详细介绍了如何搜索并下载合适的rpm包,以及如何使用rpm命令进行安装。 ... [详细]
  • Linux下部署Symfoy2对app/cache和app/logs目录的权限设置,symfoy2logs
    php教程|php手册xml文件php教程-php手册Linux下部署Symfoy2对appcache和applogs目录的权限设置,symfoy2logs黑色记事本源码,vsco ... [详细]
  • nginx+多个tomcat
    学习nginx的时候遇到的问题:nginx怎么部署两台tomcat?upstream在网上找的资源,我在nginx配置文件(nginx.conf)中添加了两个server。结果只显 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
author-avatar
美好生活的日子
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有