Log4j Vulnerability CVE-2021-45105: What You Need to Know

Log4j Vulnerability CVE-2021-45105: What You Need To Know
Table of Contents

A third Log4j2 vulnerability was disclosed the night between Dec 17 and 18 by the Apache security team, and was given the ID of CVE-2021-45105.

According to the security advisory, 2.16.0, which fixed the two previous vulnerabilities, is susceptible to a DoS attack caused by a Stack-Overflow in Context Lookups in the configuration file’s layout patterns.

What is this CVE about? What can you do to fix it? How does it differ from the previous CVEs?

Distinguishing between CVE-2021-45105 and previous Log4j CVEs

After disabling the JNDI functionality altogether, and removing the message lookup feature, 2.16.0 was thought to be unaffected by any further exploits using the Lookups in general.

However, although it prevented Remote Code Execution (RCE) and even Local Code Execution (LCE) exploits from taking place, it did not address crafted input that could manipulate the Context Lookup functionality into rendering an infinite recursion, the last leading to a stack-overflow and crash.

Background: String substitution in lookups

The StrSubstitutor and StrLookup classes in log4j-core are responsible for parsing Lookups that are made within layout patterns, such as ${ctx:username}. When the substitutor attempts to resolve the username lookup, it’ll evaluate the corresponding username variable of the ThreadContext map, and substitute it with its value (hence the name “substitutor”).

This all works, until someone messes with the values of variables in the ThreadContext Map.

At first, setting ThreadContext’s username variable to the string: ${ctx:username}, would produce the following steps for the substitutor, resulting in an infinite loop:

  • ${ctx: } tells him to look up for the value after the colon in the ThreadContext map (ctx). The value after the colon is username.
  • The substitutor will consequently find the username variable in the ThreadContext map, whose value is ${ctx:username} (yes, the same string as in the layout pattern itself. Here lies the problem), and replace the username variable name with it.
  • Now, the string in the layout pattern has remained the same, ${ctx:username}, thus the substitutor will again fetch the username variable’s value from ThreadContext, and will do it again and again in an infinite loop.

This did not crash the entire application as previously stated about CVE-2021-45046, before escalating its severity to Critical. It merely throws a java.lang.IllegalStateException, thanks to StrSubstitutor’s checkCyclicSubstitution method.

However, it was later discovered that one could use another feature of the lookup format and trigger an infinite loop that is not detected by Log4j. This will result in a java.lang.StackOverflowError, and cause an application Denial-of-Service. The vulnerable feature is the Lookup default value.

Understanding a CVE-2021-45105 Exploit

The Lookup pattern accepts the following format:

${lookupName:key:-defaultValue}

  • lookupName is the name, or type, of the lookup to perform (examples are ctx, env, etc.).
  • key is the name of the variable to look for in the corresponding map object (in ctx case, it is the ThreadContext map).
  • defaultValue is an optional value which tells the substitutor what to put in place of this lookup instance, if key does not exist in the map.

There may be readers who already figured this out:

If a variable in a ThreadContext map is attacker-controlled, one can use the default value to hold the same string as a Context Lookup. 

Here’s an example of what it looks like:

Given a vulnerable application whose Log4j configuration sets a custom layout pattern as follows:

%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} – %msg%n ${ctx:user}

And assuming an application user can control the user variable, which is stored in the ThreadContext map.
An attacker can set the value of user to the following, and trigger a stack-overflow.:

${ctx:user1:-${ctx:user}}

How does it work?

  • The substitutor finds the value of user within the context map, and replaces user with it. The value is ${ctx:user1:-${ctx:user}} as stated earlier.
  • It then tries to evaluate the resulting lookup pattern that was the value of user. This is possible because of the recursive functionality of lookup patterns.
  • user1 does not exist in ThreadContext, so the substitutor goes forward to the default value as last resort.
  • The default value is a string that in itself is a lookup pattern, ${ctx:user}, referencing back to the user variable. Note that it’s the same as the original lookup in the configuration layout pattern! This means that the substitutor will forever try to resolve the variables, calling its substitute method again and again until the stack-buffer overflows.

Other payloads were also found to trigger DoS in the same manner, one of them was mentioned by Ross Cohen, which created the ticket tracking this vulnerability:

${${::-${::-$${::-j}}}}

CVE-2021-45105: The fix

Apache have since released a fix to mitigate this vulnerability — 2.17.0, which fixed the StrSubstitutor logic, and prevented cases in which there is a recursion in lookup within the string itself:

https://github.com/apache/logging-log4j2/commit/806023265f8c905b2dd1d81fd2458f64b2ea0b5e#diff-7a09c8c292d0553c13ffe374ac767c940ff25c548f338a9a5b21ae08788d7b04

2.17.0 also restricted recursive lookups that originate from the lookup value itself:

https://github.com/apache/logging-log4j2/commit/806023265f8c905b2dd1d81fd2458f64b2ea0b5e#diff-3502b71454e47ba1f7eb4a87a739a8ab2453b9442a8199cc73e7bddf26d0cb52

Remediating CVE-2021-45105

It is highly recommended for users of Log4j to upgrade to the latest 2.17.0 version.

If it is not possible at the moment, make sure your Log4j version is at least upgraded to 2.16.0, and ensure you are not using any Context lookups of the form:

${ctx:username}

You can switch such lookups into Thread Context Map patterns, such as:

%X, %mdc, or %MDC

If Context Lookups are mandatory, ensure that there are no such lookups that reference data that is user-controlled in any way.

Staying ahead of Log4j vulnerabilities

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

  • Keep your open source components up to date to make sure direct dependencies are automatically patched to the latest version.

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.

Manage open source application risk

Recent resources

Mend.io is a Strong Performer in the Forrester Wave™ Software Composition Analysis, Q4 2024

See why Mend.io is recognized as a Strong Performer in The Forrester Wave™ Software Composition Analysis (SCA) Q4 2024 report.

Read more

Mend.io & HeroDevs Partnership: Eliminate Risks in Deprecated Package

Announcing an exclusive partnership between Mend.io and HeroDevs to provide support for deprecated packages.

Read more

All About RAG: What It Is and How to Keep It Secure

Learn about retrieval-augmented generation, one complex AI system that developers are using.

Read more