Java安全之Mojarra JSF反序列化( 二 )

定位到jsf-api-2.1.28.jar!/javax/faces/webapp/FacesServlet#service
debug, 跟进 this.lifecycle.execute(context);
public void service(ServletRequest req, ServletResponse resp) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest)req;HttpServletResponse response = (HttpServletResponse)resp;this.requestStart(request.getRequestURI());if (!this.isHttpMethodValid(request)) {response.sendError(400);} else {......FacesContext context;if (!this.initFacesContextReleased) {context = FacesContext.getCurrentInstance();if (null != context) {context.release();}this.initFacesContextReleased = true;}context = this.facesContextFactory.getFacesContext(this.servletConfig.getServletContext(), request, response, this.lifecycle);try {ResourceHandler handler = context.getApplication().getResourceHandler();if (handler.isResourceRequest(context)) {handler.handleResourceRequest(context);} else {this.lifecycle.execute(context);this.lifecycle.render(context);}}跟进this.phases[i].doPhase ,这里会有循环遍历多个Phase对象去调用doPhase方法

Java安全之Mojarra JSF反序列化

文章插图
继续跟进到this.execute
public void doPhase(FacesContext context, Lifecycle lifecycle, ListIterator<PhaseListener> listeners) {context.setCurrentPhaseId(this.getId());PhaseEvent event = null;if (listeners.hasNext()) {event = new PhaseEvent(context, this.getId(), lifecycle);}Timer timer = Timer.getInstance();if (timer != null) {timer.startTiming();}try {this.handleBeforePhase(context, listeners, event);if (!this.shouldSkip(context)) {this.execute(context);}在execute方法逻辑内 , 先通过facesContext.getExternalContext().getRequestMap();拿到一个RequestMap其中的值为ExternalContextImpl对象 , 该对象中包含了上下文、request、response等整体信息 。后续跟进 viewHandler.restoreView(facesContext, viewId);
Java安全之Mojarra JSF反序列化

文章插图
继续跟进getstate
Java安全之Mojarra JSF反序列化

文章插图
下面是一处关键点 , 通过刚才我们提到的ExternalContextImpl , 从中对应的requestParameterMap中的key取出我们传入的payload,默认情况下是javax.faces.Viewstate , 之后该值作为形参带入doGetState方法内
Java安全之Mojarra JSF反序列化

文章插图
下面是漏洞出发点的反序列化逻辑部分
先Base64解码 , 解码后通过this.guard的值是否为null判断是否有加密 , 有加密的话会去调用this.guard.decrypt进行解密 , 之后ungzip解压
Java安全之Mojarra JSF反序列化

文章插图
之后将该流转换为ApplicationObjectInputStream并有一个timeout的判断逻辑 , 最后直接反序列化
Java安全之Mojarra JSF反序列化

文章插图
存在加密的情况的话可能会有以下的配置
<context-param><param-name>javax.faces.STATE_SAVING_METHOD</param-name><param-value>client</param-value></context-param><env-entry><env-entry-name>com.sun.faces.ClientStateSavingPassword</env-entry-name><env-entry-type>java.lang.String</env-entry-type><env-entry-value>[some secret password]</env-entry-value></env-entry>
<context-param><param-name>com.sun.faces.ClientSideSecretKey</param-name><param-value>[some secret password]</param-value></context-param>

经验总结扩展阅读