Very High
CVE-2017-10271 - Oracle WebLogic Server AsyncResponseService Deserialization Vulnerability
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
CVE-2017-10271 - Oracle WebLogic Server AsyncResponseService Deserialization Vulnerability
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
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
Ratings
-
Attacker ValueVery High
-
ExploitabilityVery 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.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportRatings
-
Attacker ValueVery High
-
ExploitabilityVery 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
- .Net Framework 3.5 enabled (from add/remove features)
- 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) { // ... }; }
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
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
Exploited in the Wild
- Government or Industry Alert (https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
- News Article or Blog (https://securityaffairs.co/wordpress/129549/cyber-crime/muhstik-botnet-targeting-redis-servers-using-recently-disclosed-vulnerability.html?utm_source=rss&utm_medium=rss&utm_campaign=muhstik-botnet-targeting-redis-servers-using-recently-disclosed-vulnerability)
Would you like to delete this Exploited in the Wild Report?
Yes, delete this reportReferences
Miscellaneous
Additional Info
Technical Analysis
Report as Emergent Threat Response
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below: