SpringMVC

More on XStream RCE: SpringMVC WS

Continuing my previous post where I mentioned that the XStream RCE issue issue also affected SpringMVC RESTful WebServices using the XStream SpringOXM wrapper, I wanted to share a POC server. The code is quite simple and can be found in the XStreamServer GitHub Repo. It contains a WebService defined by the ContactController:

@Controller
@RequestMapping("/contacts")
public class ContactController {

    @Autowired
    private ContactRepository contactRepository;

    @RequestMapping( value = "/{id}", method = RequestMethod.GET )
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public final Contact get( @PathVariable( "id" ) final Long contactId ){
        System.out.println("get");
        return contactRepository.findOne(contactId);
    }

    @RequestMapping( method = RequestMethod.POST )
    @ResponseStatus( HttpStatus.CREATED )
    @ResponseBody
    public final String create( @RequestBody final Contact contact ){
        System.out.println("Contact name: " + contact.getFirstName());
        contactRepository.save((ContactImpl) contact);
        return "OK";
    }
}

The create method binds an incoming XML message with a Contact instance. This application is configured to use XStream as its binding library as shown here:

<!-- Marshaller configuration -->  
<bean id="marshallingHttpMessageConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">  
    <property name="marshaller" ref="xstreamMarshaller"/>
    <property name="unmarshaller" ref="xstreamMarshaller"/>
</bean>

<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">  
    <property name="aliases">
        <props>
            <prop key="contact">org.pwntester.springserver.ContactImpl</prop>
        </props>
    </property>
</bean>  

So SpringMVC will handle the XML document to the SpringOXM wrapper for unmarshalling. SpringOXM uses the XStreamMarshaller so it will simply call XStream in order to unmarshall the Contact object. At this point and with the details provided in the XStream RCE post its game over.

Use maven and jetty to start the server:

mvn -Djetty.port=8080 -DDebug clean jetty:run  

Expected use:

curl --header "content-type: application/xml" --data @contact.xml "http://localhost:8080/contacts"  

Exploit knowing the interface:

curl --header "content-type: application/xml" --data @exploit.xml "http://localhost:8080/contacts"  

Generic Exploit:

curl --header "content-type: application/xml" --data @exploit2.xml "http://localhost:8080/contacts"  

What to do about it

When I reported the issue to the Spring Security Team they updated their documentation and they added a CatchAllConverter for the users to use if they wish:

Documentation changes:

Jira ticket to create a new CatchAllConverter:

The main purpose of the catch-all converter class is to register itself as a catchall last converter with normal (or higher) priority, after converters that support specific domain classes. That way default XStream converters with lower priorities and possible security vulnerabilities do not get invoked.

They added the catch-all converter which is great but they did not register it by default so unless your XStreamMarshaller config looks the following, you will be in trouble:

<!-- Marshaller configuration -->  
<bean id="marshallingHttpMessageConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">  
    <property name="marshaller" ref="xstreamMarshaller"/>
    <property name="unmarshaller" ref="xstreamMarshaller"/>
</bean>

<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">  
    <property name="aliases">
        <props>
            <prop key="contact">org.pwntester.springserver.ContactImpl</prop>
        </props>
    </property>
    <property name="converters">
        <list>
            <bean class="org.springframework.oxm.xstream.CatchAllConverter"/>
            <bean class="org.pwntester.springserver.ContactConverter"/>
        </list>
    </property>
</bean>  

Please note that Spring documentation is wrong and the "CatchAllConverter" needs to be registered in the first place so it gets lower priority as showed in the XStreamMarshaller.setConverters code and not in the last place as suggested by the documentation:

    public void  [More ...] setConverters(ConverterMatcher[] converters) {
        for (int i = 0; i < converters.length; i++) {
            if (converters[i] instanceof Converter) {
                getXStream().registerConverter((Converter) converters[i], i);
            }
            else if (converters[i] instanceof SingleValueConverter) {
                getXStream().registerConverter((SingleValueConverter) converters[i], i);
            }
            else {
                throw new IllegalArgumentException("Invalid ConverterMatcher [" + converters[i] + "]");
            }
        }
    }

So summing up, if you are using XStream marshaller in your SpringMVC web service and havent set any Catch-All Converter, you are screwed. But it has an easy (undocumented) solution:

  • Write a custom converter for each of the classes you are expecting
  • Register a CatchAllConverter followed by your custom converters in the XStreamMarshaller configuration.

Thanks for reading!

SpringMVC vulnerable to XXE

While researching SpringMVC Restful APIs, I found out that any RESTful webservice built with SpringMVC and using JAXB as mashalling library to convert XML object representations to Java objects, was vulnerable to XML eXternal Entity Injection (XXE) attacks since the JAXB was configured to resolve external entities by default and it could not be configured to not do so.

SpringMVC uses SpringOXM (Object to XML Mapper) to automatically convert XML messages into Java objects so developers dont need to process the XML message and instantiate their own class instances, they just need to declare what type they are expecting in their controller method. For example:

public void createContact(Contact c) {  
    save(c);
}

So when their web method receives an XML message like:

<contact>  
    <name>John</name>
    <lastname>Smith</lastname>
    <description>Friend</description>
</contact>  

SpringOXM will automagically take the XML body and process it in order to give the developer's method a Contact instance to play with. The problem, as with any XML processing technology is that resolving XML entities can be very dangarous if processors take untrusted XML input and they are not securely configured to avoid entity resolution. More on XXE attacks.

In the SpringMVC case, SpringOXM is not an XML processing library but just a wrapper around different solutions. This allows the developer to choose between different libraries like JAXB, Castor or XStream to process the XML input. In our research, we proved that XStream wrapper (XStreamMarshaller) was not vulnerable since it does not process the DOCTYPE declaration block. Castor was vulnerable by default but the Castor wrapper (CastorMarshaller) can be configured to not resolve XML entities. However, JAXB wrapper (Jaxb1Marshaller and Jaxb2Marshaller) was vulnerable by default, and the wrapper did not exposed any property to avoid entity resolution so that any Spring webservice using JAXB as its marshalling library was vulnerable to XXE and they could do nothing about it. Unfortunately, JAXB is the most popular solution since its the Java standard so its the most widely adopted.

The issue was reported to the Spring security team that responded very quickly and came up with a patch and CVE in one month.

CVE announcement