Apache Struts, an open-source web development framework, is prone to vulnerabilities. We wrote about CVE-2017-9791 in July. The latest is CVE-2017-9805, another remote code execution flaw actively being exploited, according to reports. This vulnerability affects the Struts plug-in Representational State Transfer (REST). Apache has updated Struts with Version 2.5.13 to fix this issue. In this post we offer our analysis of this vulnerability and how the exploitation works.
Analyzing the Fix
The following screenshots show the before (Version 2.5.12, at left) and after (Version 2.5.13) of changes made to REST to fix the vulnerability.
As we can see, several changes have been made to fix this issue:
- In the fixed version “Class XStreamHandler” extends the class “AbstractContentTypeHandler.”
- The “toObject” and “fromObject” methods expect another argument of the type “ActionInvocation.” (If we check AbstractContentTypeHandler.java, “AbstractContentTypeHandler” implements the “ContentTypeHandler” class and deprecated “toObject” and “fromObject” methods.)
- The “createXstream” method has been deprecated and a new method with the same name has been defined that expects a parameter of the type “ActionInvocation,” as shown below:
This change clears the existing permission and adds as the default a per-action permission, thus preventing the issue.
Debugging the Code
Exploiting this issue requires sending a post request with specially crafted XML data to a host running Apache Struts with the vulnerable version of the REST plug-in:
Tracing the code, we can see that the request goes to ContentTypeInterceptor.java.
This function identifies the handler for the HTTP request. In this case it is “XStreamHandler,” which later calls “handler.toObject(reader, target);”. Thus control reaches to the method “toObject” in XStreamHandler.java.
This function calls the method “fromXML,” which deserializes the XML into an object:
The control next calls the method “unmarshal” in “MapConverter.java,” which creates a HashMap and populates it:
“PopulateMap” calls the method “PutCurrentEntryIntoMap,” which in turn calls the method “readItem.” The map elements here are the elements from the specially crafted XML:
The code next calls the method “doUnmarshal” in “AbstractReflectionConverter.java.” We can see that it takes the node names from the reader object and then searches for the class name, in which it was defined or declared. The code also checks whether the field exists in the class:
If the field exists in the class, then the code updates the field in the object. In the following image, the result shows the object “ImageIO$ContainsFilter” and value of its method being modified by the method “reflectionProvider.writeField”:
This process is repeated and finally the value of object becomes something like the following (truncated and reorganized for clarity). All of this came from the specially crafted XML:
The preceding object is returned by the call “readItem” in “PutCurrentEntryIntoMap” and is stored in “Object Key”:
As we see in preceding image, the code calls the method “target.put.” When the method is called, it accesses the key and value. Because they contain the crafted object, the code first calls “Nativestring.hashCode(),” which calls “Base64Data.get(),” as we see in the following call stack:
The code next calls “chooseFirstProvider()” in “Cipher.Java”:
The method serviceIterator.next() returns the object ProcessBuilder, which contains the command we provided. Because all of these objects are chained together, as we have seen, ImageIO$ContainsFilter’s method is set to java.lang.ProcessBuilder.Start(), thus executing the code.
Apache Struts is a popular web development framework and its vulnerabilities affect many deployments. We recommend users keep their Struts installations up to date with latest version.
McAfee Network Security Platform customers are protected against this vulnerability through Signature 0x45215200.