浅谈几种Bypass open_basedir的方法
0x01 open_basedir
open_basedir是php.ini中的一个配置选项,可用于将用户访问文件的活动范围限制在指定的区域。
假设open_basedir=/var/www/html/web1/:/tmp/,那么通过web1访问服务器的用户就无法获取服务器上除了/var/www/html/web1/和/tmp/这两个目录以外的文件。
注意:用open_basedir指定的限制实际上是前缀,而不是目录名。
为了演示下面的几个示例,我这里环境的open_basedir设置为Web目录和tmp目录:
测试一下,我在/home目录中新建一个1.txt文件,尝试对其进行读取,发现读取失败:
换了Web目录及其子目录和tmp目录中的文件就能成功读取,这就是open_basedir所起到的作用。
0x02 利用命令执行函数Bypass
但是open_basedir对命令执行函数没有限制,我们可以使用system()函数试一下,在前面的代码前加上system()代码来进行对比:
|
确实能够成功读到目标文件,不受open_basedir的限制:
至于其他的命令执行函数可自行尝试。
但是一般情况下,system()等命令执行函数可能会被disable_functions给禁用掉,因此运用到的场景可能并不多。
0x03 利用symlink()函数Bypass
符号链接
符号链接又叫软链接,是一类特殊的文件,这个文件包含了另一个文件的路径名(绝对路径或者相对路径)。路径可以是任意文件或目录,可以链接不同文件系统的文件。在对符号文件进行读或写操作的时候,系统会自动把该操作转换为对源文件的操作,但删除链接文件时,系统仅仅删除链接文件,而不删除源文件本身。
symlink()函数
(PHP 4, PHP 5, PHP 7)
symlink()函数创建一个从指定名称连接的现存目标文件开始的符号连接。如果成功,该函数返回TRUE;如果失败,则返回FALSE。
symlink ( string $target , string $link ) : bool
|
参数 | 描述 |
---|---|
target | 必需。连接的目标。 |
link | 必需。连接的名称。 |
当然一般情况下这个target是受限于open_basedir的。
Bypass
先给出payload,原理在后面说明,这里需要跨几层目录就需要创建几层目录:
|
访问该PHP文件后,后台便生成了两个目录和一个名为exp的符号链接:
在Web中我们直接访问exp即可读取到目标文件:
原理就是:创建一个链接文件7ea,用相对路径指向A/B/C/D,再创建一个链接文件exp指向7ea/../../../../etc/passwd。其实指向的就是A/B/C/D/../../../../etc/passwd,其实就是/etc/passwd。这时候删除7ea,再创建一个7ea目录,但exp还是指向7ea/../../../etc/passwd,所以就成功跨到/etc/passwd了。
重点在这四句:
symlink("A/B/C/D","7ea");
|
payload构造的注意点就是:要读的文件需要往前跨多少路径,就得创建多少层的子目录,然后输入多少个../来设置目标文件。
0x04 利用glob://伪协议Bypass
glob://伪协议
glob:// — 查找匹配的文件路径模式。
glob://是php自5.3.0版本起开始生效的一个用来筛选目录的伪协议,其用法示例如下:
|
Bypass
只是用glob://伪协议是无法直接绕过的,它需要结合其他函数组合利用,主要有以下两种利用方式,局限性在于它们都只能列出根目录下和open_basedir指定的目录下的文件,不能列出除前面的目录以外的目录中的文件,且不能读取文件内容。
方式1——DirectoryIterator+glob://
DirectoryIterator是php5中增加的一个类,为用户提供一个简单的查看目录的接口。
DirectoryIterator与glob://结合将无视open_basedir,列举出根目录下的文件:
|
输入glob:///*
即可列出根目录下的文件,但是会发现只能列根目录和open_basedir指定的目录的文件:
方式2——opendir()+readdir()+glob://
opendir()函数为打开目录句柄,readdir()函数为从目录句柄中读取条目。
这里结合两个函数来列举根目录中的文件:
|
效果和方式1是一样的,只能Bypass open_basedir来列举根目录中的文件,不能列举出其他非根目录和open_basedir指定的目录中的文件。
0x05 利用chdir()与ini_set()组合Bypass
基本原理
这种利用方式跟open_basedir存在缺陷的处理逻辑有关,具体原理可参考:
Bypass
测试Demo,放置在Web根目录下,在执行输入参数的PHP代码前后获取open_basedir的值看是否改变了:
|
输入以下payload:
mkdir('mi1k7ea');chdir('mi1k7ea');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo file_get_contents('/etc/passwd');
|
可以看到open_basedir被设置为’/‘了,整个失去了效果:
注意,如果php文件在Web根目录,则需要构造一个相对可上跳的open_basedir:
mkdir('mi1k7ea');
|
如果php文件直接在Web目录的子目录的话,就可不用创建相对可上跳的open_basedir了。
0x06 利用bindtextdomain()函数Bypass
bindtextdomain()函数
(PHP 4, PHP 5, PHP 7)
bindtextdomain()函数用于绑定domain到某个目录的函数。
函数定义如下:
bindtextdomain ( string $domain , string $directory ) : string
|
Bypass
利用原理是基于报错:bindtextdomain()函数的第二个参数\$directory是一个文件路径,它会在\$directory存在的时候返回\$directory,不存在则返回false。
payload:
|
成功访问到存在的文件是会返回当前文件的路径的:
若访问的文件不存在则返回false:
可以看到,和前面几种方法相比,实在是相形见绌,只能应用于判断目标文件是否存在,有利于后续和其他漏洞进行组合利用。
0x07 利用SplFileInfo::getRealPath()类方法Bypass
SplFileInfo类
(PHP 5 >= 5.1.2, PHP 7)
SplFileInfo类为单个文件的信息提供高级面向对象的接口。
SplFileInfo::getRealPath
(PHP 5 >= 5.2.2, PHP 7)
SplFileInfo::getRealPath类方法是用于获取文件的绝对路径。
Bypass
和bindtextdomain的原理一样,是基于报错的方式,返回结果都是一样的,就不再多演示,这里直接给出payload:
|
0x08 利用realpath()函数Bypass
realpath()函数
(PHP 4, PHP 5, PHP 7)
realpath — 返回规范化的绝对路径名。它可以去掉多余的../或./等跳转字符,能将相对路径转换成绝对路径。
函数定义如下:
realpath ( string $path ) : string
|
Bypass
环境条件:Windows
基本原理是基于报错返回内容的不用,设置自定义的错误处理函数,循环遍历匹配到正则的报错信息的字符来逐个拼接成存在的文件名,另外是需要结合利用Windows下的两个特殊的通配符<和>,不然只能进行暴破。
payload:
|
可以看到,首字母不同的文件就被列出来了,首字母相同的文件中只列了第一个:
0x09 脚本合集
p牛的脚本
脚本原理就是利用symlink()函数来Bypass的原理。
|