Attacker Value
Very High
(2 users assessed)
Exploitability
Very High
(2 users assessed)
User Interaction
None
Privileges Required
None
Attack Vector
Network
0

CVE-2017-10271 - Oracle WebLogic Server AsyncResponseService Deserialization Vulnerability

Disclosure Date: October 19, 2017
Exploited in the Wild
Add MITRE ATT&CK tactics and techniques that apply to this CVE.

Description

Vulnerability in the Oracle WebLogic Server component of Oracle Fusion Middleware (subcomponent: WLS Security). Supported versions that are affected are 10.3.6.0.0, 12.1.3.0.0, 12.2.1.1.0 and 12.2.1.2.0. Easily exploitable vulnerability allows unauthenticated attacker with network access via T3 to compromise Oracle WebLogic Server. Successful attacks of this vulnerability can result in takeover of Oracle WebLogic Server. CVSS 3.0 Base Score 7.5 (Availability impacts). CVSS Vector: (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H).

Add Assessment

1
Ratings
  • Attacker Value
    Very High
  • Exploitability
    Very High
Technical Analysis

Straight forward and reliable exploitation. No auth required. WebLogic is quite well known and it is also bundled in other products. Should be a pentester’s favorite.

1
Ratings
  • Attacker Value
    Very High
  • Exploitability
    Very High
Technical Analysis

Opinion

This product isn’t used everywhere, but it’s common in large enterprise networks. In some instances, it would be expected to see this product externally accessible, allowing an attacker initial access to the network.

Background

Oracle WebLogic Server (WLS) is a Java EE application server currently developed by Oracle, and it was acquired from BEA Systems in 2008. It is also bundled in other Oracle products such as Oracle Application Testing Suite, which is what the analysis is based on. By default, OATS ships with WebLogic 12.1.3.

The AsyncResponseService component in WebLogic allows a remote user to send a SOAP request that contains a malicious payload in XML format, which ends up being parsed and decoded as Java code, and result in remote code execution.

Our analysis is also based on Metasploit Framework’s pull request #11780.

Vulnerable Setup

The following is the exact setup I used to test and analyze the vulnerability:

  • Windows Server 2008 R2 x64 (other Windows systems are also supported)
    • .Net Framework 3.5 enabled (from add/remove features)
    • IE ESC (from Server Manager) disabled
    • 8GB of RAM (at least more than 4GB will be used to run OATS)
    • Duel-Core processor
  • oats-win64-full-13.3.0.1.262.zip (x86 did not work for me)
  • Jdk-7u21-windows-x64.exe
  • OracleXE112_Win64.zip (Newer version 18c did not work well for me)
  • Firefox (I had to install this because IE on Win2k8 is completely outdated)
  • Adobe Flash installed (IE ESC needs to be disabled in order to install this)

For installation instructions, please refer to the Oracle Application Testing Suite Installation Guide.

The Basics of Java Serialization

XMLEncoder

To convert a Java object in XML format, XMLEncoder is meant for that. For example:

import java.io.*;
import java.beans.XMLEncoder;
import java.util.HashMap;

public class SerializationExample {
    public static void serialize(Object obj) throws Exception {
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      XMLEncoder encoder = new XMLEncoder(outputStream);
      encoder.writeObject(obj);
      encoder.close();
      String xml = outputStream.toString();
      System.out.println(xml);
    }

    public static void main(String[] args) throws Exception {
      HashMap map = new HashMap();
      map.put("key1", "Hello World");
      serialize(map);
    }
}

The above will produce the following XML (this is saved as example.xml for decoding):

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_212" class="java.beans.XMLDecoder">
 <object class="java.util.HashMap">
  <void method="put">
   <string>key1</string>
   <string>Hello World</string>
  </void>
 </object>
</java>

To produce unsafe serialized data, you can use ysoserial. Another example (that executes system command whoami):

$ java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections1 whoami

XMLDecoder

To convert XML back to Java code, XMLDecoder is used:

import java.io.*;
import java.beans.XMLDecoder;
import java.util.HashMap;

public class DeserializationExample {
    public static Object deserialize(String xmlPath) throws Exception {
      BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(xmlPath));
      XMLDecoder decoder = new XMLDecoder(inputStream);
      Object obj = decoder.readObject();
      decoder.close();
      return obj;
    }

    @SuppressWarnings("unchecked")
    public static void main(String[] args) throws Exception {
      Object obj = deserialize("example.xml");
      HashMap map = (HashMap) obj;
      String value = map.get("key1");
      System.out.println(value);
    }
}

And this is our ouput for deserializing example.xml:

$ java DeserializationExample
Hello World

Vulnerability Analysis

Based on previous experience with Oracle Application Testing Suite, I know that WebLogic is installed as a Windows service named “Oracle ATS Server.” It listens on port 8088, which actually isn’t the same as the original WebLogic port on 7001. Since WebLogic is a Java EE application server, a proper way to debug it is using a Java decompiler (such as JD-GUI) to look at JAR files, and setting up a remote debugging environment for real-time analysis.

Remote Debugging WebLogic

To set up remote debugging for dynamic analysis, the first thing for OATS is making sure the WebLogic service is shut down. You may go to Control Panel –> Services to do that. Next, we want to modify WebLogic’s starting script, also known as startWebLogic.cmd. In this file, we want to add the following line as the first JAVA_OPTIONS:

set JAVA_OPTIONS=-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n

And then we can go ahead and start the script:

C:\OracleATS\oats\bin\startWebLogic.cmd

Note: If you have WebLogic installed (and not OATS), you might have a different directory structure, but the starting script is always named startWebLogic.cmd.

After executing the script, there should be two ports. Port 4000 is the port our debugger can attach to, and port 8088 is the WebLogic Service. At this point, you can use a debugger to connect to port 4000 and start the debugging session. In my case, I am using IntelliJ out of personal preference.

Finding AsyncResponseService Code in OATS

Finding the AsyncResponseService code is a mystery in OATS. There are a lot of JAR files, and what we want is clearly buried somewhere. Without understanding the architecture, realistically speaking we are better off digging some documentation and look for a hint there. Thankfully, Oracle does a pretty good job in documentation, so we found the following WebLogic server client type table in this write-up titled “Oracle Fusion Middleware Developing Stand-alone Clients for Oracle WebLogic Server”:

Client Type Language Protocol Client Class
WL Thin T3 Client RMI Java T3 wlthint3client.jar
WL Full Client (T3) RMI Java T3 wlfullclient.jar
WLS-LLOP RMI Java LLOP wlfullclient.jar
Thin Client RMI Java LLOP wlclient.jar
CORBA/IDL CORBA Mix LLOP No WL classes
Java SE RMI Java LLOP No WL classes
JMS Thin Client RMI Java LLOP wljmsclient.jar<br/>wlclient.jar
JMS SAF Client RMI Java LLOP wlsafclient.jar/wlthint3client.jar<br/>or<br/>wlsafclient.jar/wljmsclient.jar/wlclient.jar
JMS C Client JNI C Any wlthint3client.jar
JMS .Net Client T3 .Net T3 WebLogic.Messaging.dll
WebLogic AQ JMS Client JNDI Java LLOP/T3+ aqapi.jar, o6.jar, orail8n.jar, wlclient.jar, wlfullclient.jar, weblogic.jar, or wlthint3client.jar
JMX RMI Java LLOP wljmxclient.jar
Web Services SOAP Java HTTP/S wseeclient.jar
C++ Client CORBA C++ LLOP Tuxido lib
Tuxedo Server & Native CORBA client CORBA or RMI C++ Tuxedo-General-Inter-Orb-Protocol Tuxedo lib

Notice wseeclient.jar is for WebLogic’s SOAP service, so that’s what we to analyze. Usually I’d be pretty happy about that, except OATS doesn’t have this file (a stand-alone WebLogic server does, though). This just indicates the web service class is probably a dependency of some client when it is built, so we need to find that client.

In a different documentation (Oracle Fusion Middleware Developing JAX-WS Web Services for Oracle WebLogic Server), it mentions a JAR called com.oracle.webservices.wls.jaxws-wlswss-client_12.1.2.jar in a table:

Jar File Location Description
com.oracle.webservices.wls.jaxws-wlswss-client_12.1.2.jar ORACLE_HOME/wlserver/modules/clients/ Supports basic JAX-WS client-side functionality including:<br/><br/>* Using client-side artifacts created by both the clientgen Ant tasks.<br/>* Processing SOAP messages<br/>* Using advanced features such as web services reliable messaging, WS addressing, asynchronous request-response, and MTOM.<br/>* Using WS-Security<br/>* Using client-side SOAP message handlers<br/>* Invoking both JAX-WS and JAX-RPC web services<br/>* Using SSL

There are other ones, but the one above is more interesting, because it literally says it supports asynchronous request and response. Sounds like a match for AsyncResponseService, right? Well, by decompiling com.oracle.webservices.wls.jaxws-wlswss-client.jar, we found AsyncResponseHandler, which apparently is a valid starting point because it hits our breakpoint in IntelliJ.

The Execution Flow Map

WebLogic is quite a complex application to analyze. Although we hit the AsyncResponseHandler breakpoint, turns out it isn’t the best place because we are kind of in the middle of the code, therefore we should map out the flow a little bit to understand the whole picture. We know that our Java payload is triggering ProcessBuilder, so we can set up a breakpoint there, and understand what happened:

start:1007, ProcessBuilder (java.lang)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:71, Trampoline (sun.reflect.misc)
invoke:-1, GeneratedMethodAccessor37 (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:275, MethodUtil (sun.reflect.misc)
invokeInternal:292, Statement (java.beans)
access$000:58, Statement (java.beans)
run:185, Statement$2 (java.beans)
doPrivileged:-1, AccessController (java.security)
invoke:182, Statement (java.beans)
getValue:155, Expression (java.beans)
getValueObject:166, ObjectElementHandler (com.sun.beans.decoder)
getValueObject:123, NewElementHandler (com.sun.beans.decoder)
endElement:169, ElementHandler (com.sun.beans.decoder)
endElement:318, DocumentHandler (com.sun.beans.decoder)
endElement:609, AbstractSAXParser (com.sun.org.apache.xerces.internal.parsers)
emptyElement:183, AbstractXMLDocumentParser (com.sun.org.apache.xerces.internal.parsers)
scanStartElement:1339, XMLDocumentFragmentScannerImpl (com.sun.org.apache.xerces.internal.impl)
next:2784, XMLDocumentFragmentScannerImpl$FragmentContentDriver (com.sun.org.apache.xerces.internal.impl)
next:602, XMLDocumentScannerImpl (com.sun.org.apache.xerces.internal.impl)
scanDocument:505, XMLDocumentFragmentScannerImpl (com.sun.org.apache.xerces.internal.impl)
parse:841, XML11Configuration (com.sun.org.apache.xerces.internal.parsers)
parse:770, XML11Configuration (com.sun.org.apache.xerces.internal.parsers)
parse:141, XMLParser (com.sun.org.apache.xerces.internal.parsers)
parse:1213, AbstractSAXParser (com.sun.org.apache.xerces.internal.parsers)
parse:643, SAXParserImpl$JAXPSAXParser (com.sun.org.apache.xerces.internal.jaxp)
parse:133, WebLogicXMLReader (weblogic.xml.jaxp)
parse:173, RegistryXMLReader (weblogic.xml.jaxp)
parse:392, SAXParser (javax.xml.parsers)
run:375, DocumentHandler$1 (com.sun.beans.decoder)
run:372, DocumentHandler$1 (com.sun.beans.decoder)
doPrivileged:-1, AccessController (java.security)
doIntersectionPrivilege:80, ProtectionDomain$JavaSecurityAccessImpl (java.security)
parse:372, DocumentHandler (com.sun.beans.decoder)
run:201, XMLDecoder$1 (java.beans)
run:199, XMLDecoder$1 (java.beans)
doPrivileged:-1, AccessController (java.security)
parsingComplete:199, XMLDecoder (java.beans)
readObject:250, XMLDecoder (java.beans)
readUTF:111, WorkContextXmlInputAdapter (weblogic.wsee.workarea)
readEntry:92, WorkContextEntryImpl (weblogic.workarea.spi)
receiveRequest:179, WorkContextLocalMap (weblogic.workarea)
receiveRequest:163, WorkContextMapImpl (weblogic.workarea)
handleRequest:33, WorkAreaServerHandler (weblogic.wsee.workarea)
handleRequest:142, HandlerIterator (weblogic.wsee.handler)
dispatch:115, ServerDispatcher (weblogic.wsee.ws.dispatch.server)
invoke:80, WsSkel (weblogic.wsee.ws)
handlePost:66, SoapProcessor (weblogic.wsee.server.servlet)
process:44, SoapProcessor (weblogic.wsee.server.servlet)
run:301, BaseWSServlet$AuthorizedInvoke (weblogic.wsee.server.servlet)
service:177, BaseWSServlet (weblogic.wsee.server.servlet)
service:844, HttpServlet (javax.servlet.http)
run:280, StubSecurityHelper$ServletServiceAction (weblogic.servlet.internal)
run:254, StubSecurityHelper$ServletServiceAction (weblogic.servlet.internal)
invokeServlet:136, StubSecurityHelper (weblogic.servlet.internal)
execute:346, ServletStubImpl (weblogic.servlet.internal)
execute:243, ServletStubImpl (weblogic.servlet.internal)
wrapRun:3432, WebAppServletContext$ServletInvocationAction (weblogic.servlet.internal)
run:3402, WebAppServletContext$ServletInvocationAction (weblogic.servlet.internal)
doAs:321, AuthenticatedSubject (weblogic.security.acl.internal)
runAs:120, SecurityManager (weblogic.security.service)
run:57, WlsSubjectHandle (weblogic.servlet.provider)
doSecuredExecute:2285, WebAppServletContext (weblogic.servlet.internal)
securedExecute:2201, WebAppServletContext (weblogic.servlet.internal)
execute:2179, WebAppServletContext (weblogic.servlet.internal)
run:1572, ServletRequestImpl (weblogic.servlet.internal)
run:255, ContainerSupportProviderImpl$WlsRequestExecutor (weblogic.servlet.provider)
execute:311, ExecuteThread (weblogic.work)
run:263, ExecuteThread (weblogic.work)

By examining a number of backtraces (in SoapProcess, HandlerIterator, AsyncResponseHandler, ProcessBuilder, etc), we have roughly this map:

SoapProcess -> HandlerIterator
                      |
                      | -> AsyncResponseHandler
                      | -> WorkAreaServerHandler -> XML Parsing -> ProcessBuilder
                      | -> Other handlers (21 total)

HandlerIterator Class

In here, multiple handlers are found in the handlers variable, and each has its own handleRequest routine. The code that actually performs the iteration goes like this:

for (; this.index < this.handlers.size(); this.index++) {

  Handler handler = this.handlers.get(this.index);

  // ... Some code goes here ... //

  try {
    context.setProperty("weblogic.wsee.handler.index", new Integer(this.index));
    if (!handler.handleRequest(context)) {
      // ... some code goes here ...//

At run-time, index 12 seem to hold our AsyncResponseHandler, and 16 is the WorkAreaServerHandler.

AsyncResponseHandler Class

The purpose of the AsyncResponseHandler classs is to process an incoming async response message. It is triggered when the client sends a request to the /_async/AsyncResponseService path. The official Oracle website actually has this documented quite well:

This handler only exists within the ‘async response service’ web service. The async response service is a special service that provides an addressable endpoint for services that have been invoked asynchronously by a client on the same server instance. The async response service is layered on top of our AsyncResponseBean JWS impl. To the server hosting the async response service, an async response looks like a request, but is the async response portion of a prior async request. This handler ultimately processes this async response using the ClientDispatcher handleAsyncResponse method, which uses the handler chain’s handleResponse logic. This handler assumes that at least one handler in the client handler chain will load the SoapMessageContext.getOutParams() map with the correctly processed async response message. It then stores this response (or fault) in the ASYNC_RESPONSE_PROPERTY (or ASYNC_FAULT_PROPERTY) context variable for use by AsyncResponseBean. This last (AsyncResponseBean) is ultimately called by the ComponentHandler. The AsyncResponseBean handles looking up the proper callback method on the JWS and invoking it with the async response processed/generated by this handler.

Since the PoC is feeding the action and relates field with random values, this would cause AsyncResponseHandler to bail in the handleRequestInternal method:

WsStorage storage = WsStorageFactory.getStorage("weblogic.wsee.async.store", new AsyncInvokeStateObjectHandler());

try {
  ais = (AsyncInvokeState)storage.persistentGet(relatesTo);
  if (ais == null) {
    throw new JAXRPCException("Cannot retrieve request information for message " + relatesTo);
  }
}
catch (PersistentStoreException e) {
  if (LOGGER.isLoggable(Level.FINE))
    LOGGER.log(Level.FINE, e.getMessage(), e); 
  throw new JAXRPCException(e);
} 

WorkAreaServerHandler

The WorkAreaServerHandler is actually the one that eventually triggers XML parsing. There seems to be no official API documentation about this class, but most write-ups related to WorkAreaServerHandler are associated with CVE-2017-10271.

When HandlerIterator calls WorkAreaServerHandler’s handleRequest method, this line is most interesting:

interceptor.receiveRequest(new WorkContextXmlInputAdapter(header.getInputStream()));

A lot of things happen in this line of code. The first that happens is the getInputStream() loading up the XML code for workarea, meaning this portion here, and returns a ByteArrayInputStream:

soap_payload <<     %Q||
soap_payload <<       %Q||
soap_payload <<         %Q||
soap_payload <<           %Q||
soap_payload <<             %Q|#{string0_cmd}|
soap_payload <<           %Q||
soap_payload <<           %Q||
soap_payload <<             %Q|#{string1_param}|
soap_payload <<           %Q||
soap_payload <<           %Q||
soap_payload <<             %Q|#{shell_payload.encode(xml: :text)}|
soap_payload <<           %Q||
soap_payload <<         %Q||
soap_payload <<       %Q||
soap_payload <<       %Q||
soap_payload <<     %Q||

After getting the ByteArrayInputStream, WorkContextXmlInputAdapter loads that in XMLDecoder:

private final XMLDecoder xmlDecoder;
public WorkContextXmlInputAdapter(InputStream is) { this.xmlDecoder = new XMLDecoder(is); }

The receiveRequest will then trigger WorkContextXmlInputAdapter to read the first object defined in the XML document with the readObject method:

public String readUTF() throws IOException { return (String)this.xmlDecoder.readObject(); }

After readObject is called, a chain of reactions will occur to deserialize the XML file back to Java code, which in this case is processing the malicious ProcessBuilder code.

Patch Analysis

In WorkContextXmlInputAdapter, instead of loading the InputStream directy to XMLDecoder, some validation occurs:

public WorkContextXmlInputAdapter(InputStream var1) {
  ByteArrayOutputStream var2 = new ByteArrayOutputStream();
  try {
    boolean var3 = false;
    for (int var5 = var1.read(); var5 != -1; var5 = var1.read()) {
      var2.write(var5);
    }
  } catch (Exception var4) {
    throw new IllegalStateException("Failed to get data from input stream", var4);
  }

  this.validate(new ByteArrayInputStream(var2.toByteArray()));
  this.xmlDecoder = new XMLDecoder(new ByteArrayInputStream(var2.toByteArray()));
}

The validate method invovles checking a collection of XML tags that could be abused, such as: object, new, method, void, and array. These restrictions would easily stop ysoserial payloads:

private void validate(InputStream var1) {
  WebLogicSAXParserFactory var2 = new WebLogicSAXParserFactory();

  try {
    SAXParser var3 = var2.newSAXParser();
    var3.parse(var1, new DefaultHandler() {
      private int overallarraylength = 0;
      public void startElement(String var1, String var2, String var3, Attributes var4) throws SAXException {
        if (var3.equalsIgnoreCase("object")) {
          throw new IllegalStateException("Invalid element qName:object");
        } else if (var3.equalsIgnoreCase("new")) {
          throw new IllegalStateException("Invalid element qName:new");
        } else if (var3.equalsIgnoreCase("method")) {
          throw new IllegalStateException("Invalid element qName:method");
        } else {
          if (var3.equalsIgnoreCase("void")) {
            for(int var5 = 0; var5 < var4.getLength(); ++var5) {
              if (!"index".equalsIgnoreCase(var4.getQName(var5))) {
                throw new IllegalStateException("Invalid attribute for element void:" + var4.getQName(var5));
              }
            }
          }

          if (var3.equalsIgnoreCase("array")) {
            String var9 = var4.getValue("class");
            if (var9 != null && !var9.equalsIgnoreCase("byte")) {
              throw new IllegalStateException("The value of class attribute is not valid for array element.");
            }

            String var6 = var4.getValue("length");
            if (var6 != null) {
              try {
                int var7 = Integer.valueOf(var6);
                if (var7 >= WorkContextXmlInputAdapter.MAXARRAYLENGTH) {
                  throw new IllegalStateException("Exceed array length limitation");
                }

                this.overallarraylength += var7;
                if (this.overallarraylength >= WorkContextXmlInputAdapter.OVERALLMAXARRAYLENGTH) {
                  throw new IllegalStateException("Exceed over all array limitation.");
                }
              } catch (NumberFormatException var8) {
                // ...
              };
  }
CVSS V3 Severity and Metrics
Base Score:
7.5 High
Impact Score:
3.6
Exploitability Score:
3.9
Vector:
CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Attack Vector (AV):
Network
Attack Complexity (AC):
Low
Privileges Required (PR):
None
User Interaction (UI):
None
Scope (S):
Unchanged
Confidentiality (C):
None
Integrity (I):
None
Availability (A):
High

General Information

Vendors

  • oracle

Products

  • weblogic server 10.3.6.0.0,
  • weblogic server 12.1.3.0.0,
  • weblogic server 12.2.1.1.0,
  • weblogic server 12.2.1.2.0
Technical Analysis