Log4j CVE-2021-45105: All we know is WRONG!!

Reading Time: 3 minutes

Apache security team disclosed a third Log4j2 vulnerability the night between Dec 17 and 18 by the Apache security team. This vulnerability is termed 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, version 2.16.0 was expected 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.

Little Background Over 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, we can use another feature of the lookup format. Also, we can trigger an infinite loop that Log4j cannot detect. This will result in a java.lang.StackOverflowError, and cause an application Denial-of-Service. The vulnerable feature is the Lookup default value.

Now, lets undersand the 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 (i.e 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 the 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. It will be calling its substitute method again and again until the stack-buffer overflows.

Illustration of the crash traceback:

Crash traceback

There are other payloads which trigger DoS in the same manner. One of them is mentioned by Ross Cohen by creating the ticket tracking this vulnerability:

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

The Fix: CVE-2021-45105

Apache has since released a fix to mitigate this vulnerability — 2.17.0. This 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

Preparing ahead for Log4j Vulnerabilities

We can follow the below-listed steps to keep our dependencies updated and secure:

– We can keep our dependencies automatically patched to the latest version by using tools like WhiteSource Remediate.

– Adding 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.

Written by 

Rishabh Verma is Google Certified Professional DevOps Engineer. He is always charged up for new things & learnings. He is dedicated to his work and believes in quality output. He loves to take deep dives into cloud technologies & different tools.