Búsqueda de Ciberamenazas

Cómo funciona el exploit Log4Shell

La vulnerabilidad crítica en la utilidad de registro basada en Java Log4j de Apache (CVE-2021-44228) se ha denominado la “vulnerabilidad más importante de la última década“. También conocida como Log4Shell, el fallo ha obligado a los desarrolladores de muchos productos de software a enviar actualizaciones o mitigaciones a sus clientes. Y los responsables de Log4j han publicado dos nuevas versiones desde que se descubrió el error: la segunda elimina por completo la característica que hizo posible el exploit.

Como señalamos anteriormente, Log4Shell es una exploit de la función de “sustitución de mensajes” de Log4j, que permitió la modificación programática de los registros de eventos mediante la inserción de cadenas que requieren contenido externo. El código que soportaba esta función permitía “búsquedas” mediante las URL de la interfaz de nombres y directorios de Java (JNDI).

Esta característica, de forma inadvertida, hizo posible que un atacante insertara texto con URLs JNDI maliciosas incrustadas en solicitudes de software usando Log4j, lo que provocaba la carga de código remoto y su ejecución por parte del registrador. Para entender mejor la peligrosidad de los exploits de esta característica, analizaremos el código que lo hace posible.

Cómo funciona el registro de Log4j

Log4j genera eventos de registro usando TTCCLayout: información de tiempo, hilo, categoría y contexto. De forma predeterminada, utiliza el siguiente patrón:

  %r [%t] %-5p %c %x - %m%n

Aquí, %r muestra el tiempo transcurrido en milisegundos desde que se inició el programa, %t es el hilo, %p es la prioridad del evento, %c es la categoría, %x es el contexto de diagnóstico anidado asociado con el hilo que genera el evento, y %m es para los mensajes suministrados por la aplicación asociados con el evento. Es este último campo donde entra en juego nuestra vulnerabilidad.

La vulnerabilidad puede ser explotada cuando se llama a la función “logger.error()” con un parámetro de mensaje que incluya una URL JNDI (“jndi:dns://”, “jndi:ldap://”, o cualquiera de las otras interfaces definidas por JNDI discutidas en nuestro post anterior). Cuando se pasa esa URL, se llama a una “búsqueda” de JNDI que puede llevar a la ejecución remota de código.

Para replicar la vulnerabilidad, miramos una de las muchas pruebas de concepto que se han publicado, que replica cómo muchas aplicaciones interactúan con Log4j. En el código de logger/src/main/java/logger/App.java en esta PoC, podemos ver que llama a logger.error() con un parámetro de mensaje:

package logger;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.logger;

public class App {
    private static final Logger logger = LogManager.getLogger(App.class);
    public static void main(String[] args) {
        String msg = (args.length > 0 ? args [0] : "");
        logger.error(msg);
    }
}

Para fines de depuración, cambiamos el mensaje a una URL de prueba que usa DNS con JNDI (construido con la herramienta Interactsh) para pasarlo como parámetro a la función “logger.error ()” y lo pasamos por el programa:

Podemos ver que después de llamar al método “logger.error ()” desde la clase “AbstractLogger” con la URL proporcionada, se llama a otro método que es “logMessage”:

El método log.message crea un objeto mensaje con la URL proporcionada:

A continuación, llama a “processLogEvent” de la clase “LoggerConfig”, para registrar el evento:

Luego llama al método “append” de la clase “AbstractOutputStreamAppender”, que agrega el mensaje al registro:

Llegamos al problema

Esto, a su vez, llama al método “directEncodeEvent”:

El método directEncodeEvent, a su vez, llama al método “getLayout().Encode”, que formatea el mensaje de registro y agrega el parámetro proporcionado, que en este caso es la URL del exploit de prueba:

Luego crea un nuevo objeto “StringBuilder”:

StringBuilder llama al método “format” de la clase “MessagePatternConvert” y analiza la URL suministrada, busca ‘$’ y ‘{‘ para identificar la URL:

Después intenta identificar varios nombres y valores que están separados por ‘:’ o ‘-‘:

Luego llama al método “resolveVariable” de la clase “StrSubstitutor” que identificará las Variables, pueden ser cualquiera de las siguientes:

{date, java, marker, ctx, lower, upper, jndi, main, jvmrunargs, sys, env, log4j}

El código llama entonces al método “lookup” de la clase “Interpolator”, que comprobará el servicio asociado a la variable (en este caso, “jndi”):

Al encontrar “jndi”, llama al método “lookup” de la clase “jndiManager”, que evalúa el valor del recurso JNDI:

Después, llama a “getURLOrDefaultInitCtx” de la clase “IntialContext”. Aquí es donde se crea la petición que se enviará a la interfaz JNDI para recuperar el contexto, en función de la URL proporcionada. Aquí es donde el exploit empieza a funcionar.  En este caso, la URL está en DNS:

En el caso de una URL en DNS, como esta dispara, podemos ver una consulta DNS a la URL proporcionada con Wireshark:

(Esta es una URL de prueba, y no es realmente maliciosa)

Si la URL es ‘jndi:ldap://’ llama a otro método de la clase “ldapURLConext” para comprobar si la URL tiene “queryComponents”:

Después llama al método “lookup” de la clase “ldapURLContext”, la variable “name” contiene la URL de ldap:

Esto a su vez se conecta con el ldap ” proporcionado:

Luego se llama al método “flushBuffer” de la clase “OutputStreamManager”, aquí ‘buf’ contiene los datos devueltos por el servidor LDAP, en este caso la cadena “mmm….” que vemos a continuación:

Mirando la captura del paquete en Wireshark, vemos que la petición tiene los siguientes bytes:

Estos son los datos serializados y serán mostrados por el cliente como podemos ver a continuación, lo que demuestra que la vulnerabilidad fue explotada, observe la cadena [main] ERROR logger .App” en el mensaje seguido de los datos:

La solución

Todo esto fue posible porque en todas las versiones de Log4j 2 hasta la versión 2.14 (excluyendo la versión de seguridad 2.12.2), el soporte JNDI no estaba restringido en términos de qué nombres podían resolverse. Algunos protocolos no eran seguros o pueden permitir la ejecución remota de código. Log4j 2.15.0 restringió JNDI a solo búsquedas LDAP, y esas búsquedas están limitadas a conectarse a objetos primitivos Java en el host local por defecto.

Sin embargo, la corrección de la versión 2.15.0 dejó la vulnerabilidad parcialmente sin resolver, ya que en las implementaciones con “ciertos patrones de diseño no predeterminados” para Log4j, incluidos aquellos con búsquedas de contexto (como “$${ctx:loginId}”) o un patrón de mapa de contexto de hilo (“%X”, “%mdc” o “%MDC”), todavía era posible elaborar datos de entrada maliciosos utilizando un patrón de búsqueda de JNDI que diera lugar a un ataque de denegación de servicio (DOS). En las últimas versiones, se han desactivado todas las búsquedas por defecto. Esto cierra la función JNDI por completo, pero asegura a Log4j contra la explotación remota.

Conclusión

Log4j es un marco de registro muy popular y utilizado por una cantidad significativa de productos de software muy utilizados, servicios en la nube y otras aplicaciones. Las vulnerabilidades en las versiones anteriores a 2.15.0 hacen posible que un actor malintencionado recupere datos de una aplicación afectada o su sistema operativo subyacente, o ejecute código Java que se ejecuta con los permisos otorgados al tiempo de ejecución de Java (Java.exe en Windows sistemas). Este código puede ejecutar comandos y scripts contra el sistema operativo local, que a su vez pueden descargar código malicioso adicional y proporcionar una ruta para la elevación de privilegios y acceso remoto persistente.

Si bien la versión 2.15.0 de Log4j, eliminada en el momento en que la vulnerabilidad se hizo pública, soluciona estos problemas, todavía deja a los sistemas vulnerables en algunos casos a ataques de denegación de servicio y exploits (solucionados al menos parcialmente por 2.16.0). El 18 de diciembre, se lanzó una tercera versión nueva, 2.17.0, para prevenir ataques recursivos que podrían causar una denegación de servicio). Las organizaciones deben evaluar qué versiones de Log4j hay en sus aplicaciones desarrolladas internamente y aplicar parches a las versiones más recientes (2.12.2 para Java 7 y 2.17.0 para Java 8), y aplicar los parches del software comercial que utilicen a medida que estén disponibles.

Sophos proporciona cobertura para los comportamientos de red y las cargas útiles asociadas con esta vulnerabilidad, como se detalla a continuación:

AV:

  • Troj/JavaDl-AAN
  • Troj/Java-AIN
  • Troj/BatDl-GR
  • Mal/JavaKC-B
  • XMRig Miner (PUA)
  • Troj/Bckdr-RYB
  • Troj/PSDl-LR
  • Mal/ShellDl-A
  • Linux/DDoS-DT
  • Linux/DDoS-DS
  • Linux/Miner-ADG
  • Linux/Miner-ZS
  • Linux/Miner-WU
  • Linux/Rootkt-M

IPS:

Sophos Firewall:

  • SIDs : 2306426, 2306427, 2306428, 58722, 58723, 58724, 58725, 58726, 58727, 58728, 58729, 58730, 58731, 58732, 58733, 58734, 58735, 58736, 58737, 58738, 58739, 58740, 58741, 58742, 58743, 58744, 58751, 58784, 58785, 58786, 58787, 58788, 58789, 58790, 58795

Sophos Endpoint

  • SIDs:  2306426, 2306427, 2306428, 2306438, 2306439, 2306440, 2306441

Sophos SG UTM

  • SIDs: 58722, 58723, 58724, 58725, 58726, 58727, 58728, 58729, 58730, 58731, 58732, 58733, 58734, 58735, 58736, 58737, 58738, 58739, 58740, 58741, 58742, 58743, 58744, 58751, 58784, 58785, 58786, 58787, 58788, 58789, 58790, 58795