代码审计系列之EOS开发框架
EOS框架简介
Primeton EOS(Entriprise Operation System),以下简称EOS,是基于J2EE平台、采用面向构件技术实现企业级应用开发、运行、管理、监控、维护的中间件平台。EOS6是普元公司推出的基于SOA架构,支持SCA1.0、SDO2.1规范的新一代EOS产品。基于EOS6开发的应用具备符合国际标准,易于扩展,易于集成的特性。
参考链接:http://www.primeton.com/products/ep/overview.php
前言
今天要说的是一个通用型的开发框架,涉及领域比较宽泛,不讲解漏洞,只讲解整个框架的调用流程
默认配置&&安全
根据官网给出来的默认配置如下web.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
id="defaultWebApp"
version="2.4"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<context-param>
<param-name>rest.config.locations</param-name>
<param-value>
classpath*:META-INF/services/restconfig.properties
</param-value>
</context-param>
<context-param>
<param-name>resteasy.injector.factory</param-name>
<param-value>com.primeton.components.rest.extend.JSONInjectorFactoryImpl</param-value>
</context-param>
<filter>
<filter-name>InterceptorFilter</filter-name>
<filter-class>com.eos.access.http.InterceptorFilter</filter-class>
</filter>
<listener>
<listener-class>com.primeton.sca.host.webapp.SCAWebServiceServletListener</listener-class>
</listener>
<listener>
<listener-class>com.primeton.ext.runtime.core.RuntimeJ2EEHost</listener-class>
</listener>
<listener>
<listener-class>com.primeton.engine.core.impl.process.SessionListener</listener-class>
</listener>
<listener>
<listener-class>com.eos.access.http.UserObjectSessionListener</listener-class>
</listener>
<filter-mapping>
<filter-name>InterceptorFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
<servlet>
<servlet-name>ControllerServlet</servlet-name>
<servlet-class>
com.eos.access.http.ControllerServlet
</servlet-class>
<load-on-startup>10</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/WSActivityInstManagerService</url-pattern>
</servlet-mapping>
......
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/WSWorklistQueryManagerService</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>*.flow</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>*.flowx</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>*.gzip</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>*.ajax</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>*.beanx</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>*.debug</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/common.remote</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>*.precompile</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>*.ext</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>*.terminate</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>resteasyservlet</servlet-name>
<servlet-class>
com.primeton.components.rest.extend.CustomHttpServletDispatcher
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>resteasyservlet</servlet-name>
<url-pattern>/rest/services/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>coframe/index.jsp</welcome-file>
</welcome-file-list>
<mime-mapping>
<extension>xml</extension>
<mime-type>application/xml</mime-type>
</mime-mapping>
<mime-mapping>
<extension>war</extension>
<mime-type>application/zip</mime-type>
</mime-mapping>
<mime-mapping>
<extension>ear</extension>
<mime-type>application/zip</mime-type>
</mime-mapping>
<mime-mapping>
<extension>zip</extension>
<mime-type>application/zip</mime-type>
</mime-mapping>
<error-page>
<error-code>404</error-code>
<location>/common/notFound.jsp</location>
</error-page>
<!--
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<page-encoding>UTF-8</page-encoding>
</jsp-property-group>
</jsp-config>
-->
</web-app>
上面的配置效果,再加上程序开发者的拿来主义习惯,可能会造成以下2个问题
- webservice接口泄露
- rest接口泄露
rest接口是通过一个配置文件映射(restconfig.properties):
resteasy.resources=com.primeton.bps.web.control.restful.WebControlRestService
@Path("/rest/services/bps/webcontrol")
@Consumes({"application/json", "application/x-www-form-urlencoded"})
@Produces({"application/json"})
public class WebControlRestService {
public WebControlRestService() {
}
@POST
@Path("/queryParticipants")
public Map<String, Object> queryParticipants(HashMap<String, Object> mapObject) throws WFServiceException, JSONException {
HashMap resultMap = new HashMap();
String nodeBody = String.valueOf(getJsonFromMap(mapObject));
DataObject node = (DataObject) changeToDataObject("node", nodeBody, Boolean.valueOf(true));
DataObject otherParamObj = (DataObject) changeToDataObject("otherParamObj", nodeBody, Boolean.valueOf(true));
resultMap.put("childNodes", ServiceUtil.queryParticipants(node, otherParamObj))
return resultMap;
}
@POST
@Path("/searchParticipants")
public Map<String, Object> searchParticipants(HashMap<String, Object> mapObject) throws WFServiceException {
HashMap resultMap = new HashMap();
String name = String.valueOf(mapObject.get("name"));
Map extData = (Map) mapObject.get("extData");
PageCond page = (PageCond) mapObject.get("page");
resultMap.put("childNodes", ServiceUtil.searchParticipants(name, extData, page)
return resultMap;
}
..................
..................
调用方式参数通过json传递
POST /rest/services/bps/webcontrol/queryProcessAndActivity HTTP/1.1
Host: localhost: 8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/ 20100101 Firefox/56.
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.
Accept-Language: en-US,en;q=0.
Content-Type: application/json
Content-Length: 16
Cookie:
Connection: close
Upgrade-Insecure-Requests: 1
{"workItemID":1}
webservice接口就相对简单了这里总共有十处
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/WSActivityInstManagerService</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/WSAgentManagerService</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/WSAppointActivityManagerService</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/WSBackActivityManagerService</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/WSDefinitionQueryManagerService</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/WSDelegateManagerService</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/WSFreeFlowManagerService</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/WSProcessInstManagerService</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/WSRelativeDataManagerService</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/WSWorkItemDrawbackManagerService</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/WSWorkItemManagerService</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/WSWorklistQueryManagerService</url-pattern>
</servlet-mapping>
调用方式可以通过wsdl进行解析
访问http://localhost:8080/WSActivityInstManagerService?wsdl
注意:EOS框架整体上来讲都是需要授权登陆的,只有这两个接口调用方式不需要授权,webservice通过ControllerServlet进行映射,这里一章节放到框架讲解里面
EOS框架讲解
整个框架通过拦截器com.eos.access.http.InterceptorFilter和com.eos.access.http.ControllerServlet来实现的
拦截器的调用流程
初始化子拦截器
public void init(FilterConfig arg0) throws ServletException {
this.init();
}
public void init() {
logger.info("init InterceptorFilter.");
Map processors = RequstProcessors.INSTANCE.getAllProcessors();
Iterator e = processors.entrySet().iterator();
while (e.hasNext()) {
Entry interceptors = (Entry) e.next();
WebInterceptorConfig i$ = new WebInterceptorConfig();
i$.setFilterId("ProcessorInterceptor_" + (String) interceptors.getKey());
i$.setSortIdx(2147483647);
i$.setPattern((String) interceptors.getKey());
i$.setClassName(ProcessorWebInterceptor.class.getName());
WebInterceptorManager.INSTANCE.addInterceptorConfig(i$);
ProcessorWebInterceptor interceptor = (ProcessorWebInterceptor) WebInterceptorManager.INSTANCE.getInterceptor("ProcessorInterceptor_" + (String) interceptors.getKey())
if (interceptor != null) {
interceptor.setProcessor((IProcessor) interceptors.getValue());
}
}
try {
Field e1 = ClassUtil.getField(WebInterceptorManager.class, "interceptors");
e1.setAccessible(true);
List interceptors1 = (List) e1.get(WebInterceptorManager.INSTANCE);
Iterator i$1 = interceptors1.iterator();
while (i$1.hasNext()) {
IWebInterceptor interceptor1 = (IWebInterceptor) i$1.next();
if (interceptor1 instanceof ILifeCycleWebInterceptor) {
try {
((ILifeCycleWebInterceptor) interceptor1).init();
} catch (Throwable var7) {
logger.error(var7);
}
}
}
} catch (Throwable var8) {
logger.error(var8);
}
}
里面两次初始化了调用链对象
第一层:
Map processors = RequstProcessors.INSTANCE.getAllProcessors();
跟进代码分析:
public static final RequstProcessors INSTANCE = new RequstProcessors();
private RequstProcessors() {
File configDir = new File(ApplicationContext.getInstance().getApplicationConfigPath());
HandlerRegistry processorRegistry = HandlerRegistry.load(RequstProcessors.class.getClassLoader, configDir, "handler-processor.xml", IProcessor.class, "handler", "id", "class", "sortIdx", 0.false);
Iterator i$ = processorRegistry.getEffectiveHandlerModels().iterator();
while (i$.hasNext()) {
HandlerModel model = (HandlerModel) i$.next();
Element handlerElement = model.getHandlerElement();
String suffix = handlerElement.getAttribute("suffix");
String processorClassName = handlerElement.getAttribute("class");
StringTokenizer stk = new StringTokenizer(suffix, ",");
try {
while (stk.hasMoreElements()) {
String e = stk.nextToken();
IProcessor processor = (IProcessor) Class.forName(processorClassName).newInstance();
processor.setRequestSuffix(e);
logger.info("the url suffix with [" + e + "] will be processed by [" + processorClassName + "].");
this.requestProcessors.put(e, processor);
}
} catch (Throwable var11) {
logger.error("create processor instance fail!", var11);
}
}
}
这里进行了配置文件的解析,并且初始化了配置信息,映射信息来自handler-processor.xml
<?xml version="1.0" encoding="UTF-8"?>
<handlers>
<handler id="flowProcessor" suffix=".flow" sortIdx="0"
class="com.primeton.ext.engine.core.processor.HttpPageFlowProcessor"/>
<handler id="actionProcessor" suffix=".action" sortIdx="0"
class="com.primeton.ext.engine.core.processor.ActionProcessor"/>
<handler id="downloadProcessor" suffix=".download" sortIdx="0"
class="com.primeton.access.http.impl.processor.DownloadProcessor"/>
<handler id="downloadConfigProcessor" suffix=".configdownload" sortIdx="0"
class="com.primeton.access.http.impl.processor.DownloadConfigProcessor"/>
<handler id="ConfigurationDownloadProcessor" suffix=".governordownload" sortIdx="0"
class="com.primeton.access.http.impl.processor.GovernorDownloadConfigProcessor"/>
<handler id="GovernorDownloadConfigurationProcessor" suffix=".governordownload" sortIdx="0"
class="com.primeton.access.http.impl.processor.GovernorDownloadConfigProcessor"/>
<!-- <handler id="ajaxFlowProcessor" suffix=".flow.ajax,.flowx.ajax" sortIdx="0"
class="com.primeton.ext.engine.core.processor.AjaxPageflowProcessor" /> -->
<handler id="commonServiceProcessor" suffix=".remote" sortIdx="0"
class="com.primeton.access.client.impl.processor.CommonServiceProcessor"/>
<handler id="ajaxBizProcessor" suffix=".biz.ajax" sortIdx="0"
class="com.primeton.ext.engine.core.processor.AjaxBizProcessor"/>
...............
...............
第二层:
ProcessorWebInterceptor interceptor = (ProcessorWebInterceptor)WebInterceptorManager.INSTANCE.getInterceptor("ProcessorInterceptor_" + (String)interceptors.getKey());
跟进代码进行分析:
public static final WebInterceptorManager INSTANCE = new WebInterceptorManager();
private List<WebInterceptorConfig> configs = new ArrayList();
private List<IWebInterceptor> interceptors = new ArrayList();
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
private WebInterceptorManager() {
this.readConfigFile();
}
private void readConfigFile() {
File configDir = new File(ApplicationContext.getInstance().getApplicationConfigPath());
HandlerRegistry inteceptorRegistry = HandlerRegistry.load(WebInterceptorManager.class.getClassLoader(), configDir, "handler-web.xml", IWebInterceptor.class, "handler", "id", "class", "sortIdx", 100, false);
Iterator i$ = inteceptorRegistry.getEffectiveHandlerModels().iterator();
while (i$.hasNext()) {
HandlerModel model = (HandlerModel) i$.next();
try {
Element e = model.getHandlerElement();
String className = e.getAttribute("class");
String pattern = e.getAttribute("pattern");
WebInterceptorConfig config = new WebInterceptorConfig();
config.setFilterId(model.getId());
config.setClassName(className);
if (pattern != null && !pattern.equals("")) {
config.setPattern(pattern);
}
int idx = model.getIndex();
if (idx < 0) {
logger.error("the WebInterceptor\'s sortIdx not allow lower than 0!");
} else {
config.setSortIdx(idx);
this.addInterceptorConfig(config);
}
} catch (Exception var10) {
logger.error("the WebInterceptor config has error!", var10);
}
}
}
public void addInterceptorConfig(WebInterceptorConfig config) {
this.lock.writeLock().lock();
try {
IWebInterceptor interceptor = this.createInterceptor(config.getClassName());
int idx;
int willAddPos;
int i;
if (config.isDefaultSortIdx()) {
idx = 0;
boolean e = false;
willAddPos = 0;
for (i = this.configs.size(); willAddPos < i; ++willAddPos) {
WebInterceptorConfig length = (WebInterceptorConfig) this.configs.get(willAddPos);
if (length.getSortIdx() > idx && !length.isDefaultSortIdx()) {
this.configs.add(idx, config);
this.interceptors.add(idx, interceptor);
在这个类中我们初始化了handler-web.xml里面对应的拦截器,存放在参数this.interceptors里面
<?xml version="1.0" encoding="UTF-8"?>
<handlers>
<handler id="WSInterceptor" sortIdx="0" pattern="/*"
class="com.primeton.sca.host.webapp.SCAWebServiceServletFilter"/>
<handler id="WebI18NInterceptor" sortIdx="1" pattern="/*" class="com.primeton.access.http.impl.WebI18NInterceptor"/>
<handler id="HttpSecurityWebInterceptor" sortIdx="2" pattern="*.flow,*.jsp"
class="com.eos.access.http.security.HttpSecurityWebInterceptor"/>
<handler id="HttpRefererWebInterceptor" sortIdx="3" pattern="/*"
class="com.eos.access.http.security.HttpRefererWebInterceptor"/>
<handler id="UserLoginInterceptor" sortIdx="100" pattern="/*" class="com.eos.access.http.UserLoginCheckedFilter"/>
<handler id="AccessedResourceInterceptor" sortIdx="101" pattern="/*"
class="com.primeton.access.authorization.impl.AccessedHttpResourceFilter"/>
</handlers>
跟进调用doFilter
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) arg0;
HttpServletResponse response = (HttpServletResponse) arg1;
request.setCharacterEncoding(MultipartResolver.getEncoding());
if (UserLoginCheckedFilter.isPortal() && request.getSession().getAttribute("userObject") == null) {
IUserObject session = CustomObjectProviderProvider.getProvider().getVirtualUserObject(VirtualUserObjectTypes.PORTAL_USER);
((UserObject) session).setUserRemoteIP(HttpHelper.getRemoteAddr(request));
((UserObject) session).setSessionId(request.getSession().getId());
((UserObject) session).setUniqueId(request.getSession().getId());
OnlineUserManager.login(session);
request.getSession().setAttribute("userObject", session);
} ............... ............... ...............try {
Throwable e;
try {
if (var15 != null) {
muo = MUODataContextHelper.create(var15);
DataContextManager.current().setMUODataContext(muo);
}
WebInterceptorChainImpl var16 = (WebInterceptorChainImpl) WebInterceptorManager.INSTANCE.createChain(request, response);
var16.setFilterChain(filterChain);
var16.doIntercept(request, response);
这里追踪一下doIntercept方法
public class WebInterceptorChainImpl implements IWebInterceptorChain {
private List<IWebInterceptor> interceptors = new ArrayList();
private static final int DEFAULT_IDX = -1;
private int pos = 0;
private FilterChain filterChain;
public WebInterceptorChainImpl() {
}
public void doIntercept(HttpServletRequest servletrequest, HttpServletResponse servletresponse) throws IOException, ServletException {
if (this.pos == this.interceptors.size()) {
if (this.filterChain != null) {
this.filterChain.doFilter(servletrequest, servletresponse);
}
} else {
IWebInterceptor interceptor = (IWebInterceptor) this.interceptors.get(this.pos);
++this.pos;
interceptor.doIntercept(servletrequest, servletresponse, this);
}
}
从最后一句可以看出来,不管任何一个请求,都要遍历两个映射文件对应的拦截器的doIntercept
上面是一个整体的调用逻辑,说明一下这几个filter的作用
- WSInterceptor webservice的请求路由
- WebI18NInterceptor
- HttpSecurityWebInterceptor 整体的框架路由
- HttpRefererWebInterceptor referer检查
- UserLoginInterceptor 登陆检查
- AccessedResourceInterceptor 资源的访问权限检查
webservice直接forward到了axis2请求上的
public void doIntercept(HttpServletRequest request, HttpServletResponse response, IWebInterceptorChain chain) throws IOException, ServletException { .......... ..........
..........
..........
RequestDispatcher dispatcher = WebAppServletHost.getInstance().getRequestDispatcher(path);
if (dispatcher != null) {
String url = request.getRequestURL().toString();
String serviceName;
try {
URI contextPath = new URI(url);
serviceName = contextPath.getHost();
String e = this.getIpAddress();
url = url.replaceAll(serviceName, e);
} catch (URISyntaxException var14) {
;
}
String contextPath1 = request.getContextPath();
if (StringUtils.isEmpty(contextPath1)) {
serviceName = this.getServiceName(url);
} else {
serviceName = this.getServiceName(contextPath1, url);
}
try {
IDataObjectType e1 = DataObjectTypeFactory.createDataObjectType();
String servicePath = AegisFileCache.getFilePath(serviceName);
Class clazz = WebServiceMapping.getServiceClass(serviceName);
DefinitionFactory.createDefinition(serviceName, servicePath, clazz, url, e1);
} catch (Exception var13) {
var13.printStackTrace();
}
dispatcher.forward(request, response);
整体的框架调用基本上分析完了,接下来就是业务层的东西,重点看handler-processor.xml
<handler id="flowProcessor" suffix=".flow" sortIdx="0" class="com.primeton.ext.engine.core.processor.HttpPageFlowProcessor" />
public class HttpPageFlowProcessor extends AbstractPageFlowProcessor {
public HttpPageFlowProcessor() {
}
public IParameterSet createParameterSet(HttpServletRequest request, HttpServletResponse response) {
return ParameterBuilder.createHttpParamSet(request);
}
}
分析父类解析参数规则:
public abstract class AbstractPageFlowProcessor extends AbstractProcessor {
private static final Logger logger = TraceLoggerFactory.getLogger(AbstractPageFlowProcessor.class);
public static final String CACHE_EOS_PAGEFLOW = "_eosFlowCache";
public static final String EOS_PAGEFLOW_KEY = "_eosFlowKey";
public static final String EOS_PAGEFLOW_ACTION = "_eosFlowAction";
public static final String EOS_PAGEFLOW_AJAX = "_eosAjax";
public static final String EOS_CURRENT_PAGEFLOW_INSTANCE = "_eosCurrentInstanceID";
public static final String EOS_PARENT_PAGEFLOW_INSTANCE = "_eosParentInstanceID";
public static final String EOS_PAGEFLOW_DATACONTEXT = "_eosFlowDataContext";
public static final String EOS_REQUEST_DATACONTEXT = "_eosRequestDataContext";
private static final List<String> innerVariables = new ArrayList();
public AbstractPageFlowProcessor() {
}
public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
try {
HttpServletRequest e = MultipartResolverFactory.getMultipartResolver().resolveMultipart(request);
HttpMapContextFactory config1 = new HttpMapContextFactory(e, response);
DataContextManager.current().setMapContextFactory(config1);
this.doProcess(e, response, (IParameterSet) null);
继续跟进这里面的doProcess
public abstract class AbstractPageFlowProcessor extends AbstractProcessor {
.....
public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
try {
.....
this.doProcess(e, response, (IParameterSet) null);
} catch (Throwable var7) {
.....
}
}
public void doProcess(HttpServletRequest request, HttpServletResponse response, IParameterSet parameterSetA) throws IOException, ServletException {
.....
try {
.....
Object var35 = request.getAttribute("_eosRequestDataContext");
if (var35 != null && var35 instanceof PageflowRuntimeContext) {
.....
.....
if (!this.hasUserDataConvert(pageFlowInstance, var32.getStateName(), current_error_uri, this.getRequestedFlowID(request))) {
IParameterSet var42 = this.createParameterSet(request, response);
IVariable[] var44 = this.moveInnerParams((IVariable[]) var38);
var42.build(var44, context);
build函数里面对参数_eosRequestDataContext进行了解析:
public void build(IVariable[] vars, IDataContext context) {
Map serverTypeMapping = this.getTypeMapping(vars);
IVariable _eosFlowKey;
Class _eosFlowDataContext;
for (int customerTypeMapping = 0; customerTypeMapping < this.customVars.size(); ++customerTypeMapping) {
IVariable typeMapping = (IVariable) this.customVars.get(customerTypeMapping);
String varsMap = typeMapping.getName();
_eosFlowKey = this.getDefinedVariable(varsMap, vars);
if (_eosFlowKey != null) {
_eosFlowDataContext = _eosFlowKey.getTypeClass();
if (varsMap.equals(_eosFlowKey.getName()) && (_eosFlowKey.isDataObject() || isConcreteClass(_eosFlowDataContext))) {
this.customVars.remove(customerTypeMapping);
--customerTypeMapping;
}
} else {
this.customVars.remove(customerTypeMapping);
--customerTypeMapping;
}
}
Map var26 = this.getTypeMapping((IVariable[]) this.customVars.toArray(new IVariable[this.customVars.size()]));
HashMap var25 = new HashMap(serverTypeMapping);
var25.putAll(var26);
context.setTypeMappings(var25);
HashMap var27 = new HashMap();
if (vars != null) {
IVariable[] var28 = vars;
int var30 = vars.length;
for (int _eosFlowAction = 0; _eosFlowAction < var30; ++_eosFlowAction) {
IVariable xpathSorter = var28[_eosFlowAction];
String fixedMap = xpathSorter.getName();
var27.put(fixedMap, xpathSorter);
}
}
Iterator var29 = this.customVars.iterator();
String var34;
while (var29.hasNext()) {
IVariable var32 = (IVariable) var29.next();
var34 = var32.getName();
var27.put(var34, var32);
}
_eosFlowKey = null;
String var31;
try {
var31 = (String) this.values.get("_eosFlowKey");
} catch (Exception var22) {
throw new RuntimeException("process _eosFlowKey has exception!", var22);
}
_eosFlowDataContext = null;
String var33;
try {
var33 = (String) this.values.get("_eosFlowDataContext");
} catch (Exception var21) {
throw new RuntimeException("process _eosFlowDataContext has exception!", var21);
}
var34 = null;
try {
var34 = (String) this.values.get("_eosFlowAction");
} catch (Exception var20) {
throw new RuntimeException("process _eosFlowAction has exception!", var20);
}
if (var34 != null && !var34.equals("")) {
context.set("_eosFlowAction", var34);
}
if (var31 != null && !var31.equals("")) {
context.set("_eosFlowKey", var31);
}
Iterator i$;
Map var37;
if (var33 != null && !var33.equals("")) {
try {
Object var35 = ContextSerializer.deserialize(var33);
最终代码逻辑流入了反序列化里面:
public static Object deserialize(String codedString) throws IOException, ClassNotFoundException {
BASE64Decoder decoder = new BASE64Decoder();
byte[] buf = decoder.decodeBuffer(codedString);
ByteArrayInputStream bais = new ByteArrayInputStream(buf);
ObjectInputStream oos = new ObjectInputStream(bais);
Object o = oos.readObject();
oos.close();
return o;
}
继续跟进分析第二点:
<handler id="ajaxBizProcessor" suffix=".biz.ajax" sortIdx="0" class="com.primeton.ext.engine.core.processor.AjaxBizProcessor" />
映射类里面代码逻辑如下:
public class AjaxBizProcessor extends AbstractBizProcessor {
public AjaxBizProcessor() {
}
public IParameterSet createParameterSet(HttpServletRequest request, HttpServletResponse response) {
response.setCharacterEncoding("UTF-8");
return ParameterBuilder.createAjaxParamSet(request);
}
}
public static IParameterSet createAjaxParamSet(HttpServletRequest request) {
AjaxParameterSet parameterSet = new AjaxParameterSet();
parameterSet.setRequest(request);
buildParameterSet(parameterSet);
return parameterSet;
}
private static void buildParameterSet(AbstractParameterSet parameterSet) {
parameterSet.init();
}
public void init() {
new StringBuffer();
StringBuffer buffer;
String e;
try {
e = this.request.getParameter("__ajaxParam");
if (e == null) {
e = "<root><params></params><data></data></root>";
}
buffer = new StringBuffer(URLDecoder.decode(e, "UTF-8"));
} catch (UnsupportedEncodingException var10) {
logger.error(var10);
throw new RuntimeException(var10);
}
try {
if (buffer.toString().trim().equals("")) {
buffer.append("<root><params></params><data></data></root>");
}
e = null;
String xml = buffer.toString();
Document var11;
if (!getXMLHeader(xml).contains("encoding")) {
String paramNode = MultipartResolver.getEncoding();
var11 = XmlUtil.parseStringThrowsException(xml, paramNode);
} else {
var11 = XmlUtil.parseString(xml);
}
这里进行了xml的解析,没有进行实体的转换
根据后台日志信息可以看出来跟我们分析的一样,报错文件找不到,说明xxe执行
总结
- 发布网站要避免默认配置保留发布
- 对于rest定义的接口尽量采用复杂命名,防止由于默认配置导致的接口泄露
- 禁用xml解析库的实体,如果从第三方库没有禁用标志,可以尝试在filter里面添加
- 对于反序列化漏洞,升级jdk,或者相关反序列化调用的类库