Log4j Recursive Lookup Vulnerabilities Explained

Apache Log4j’s lookup evaluation mechanism, designed to dynamically resolve configuration properties and runtime variables, introduced critical security flaws when handling nested and recursive expressions. This article examines the security vulnerabilities caused by recursive lookup evaluation within Log4j XML configurations—specifically Remote Code Execution (RCE) and Denial of Service (DoS)—and details how configuration design patterns allowed malicious inputs to trigger these flaws.

The Mechanism of Recursive Lookup Evaluation

Log4j uses a string substitution engine called StrSubstitutor to parse expressions enclosed in ${...}. When a lookup prefix (such as jndi:, ctx:, or env:) is detected, Log4j evaluates the expression and replaces it with the corresponding value.

In vulnerable versions, this evaluation was performed recursively. If the resolved value itself contained another ${...} expression, the engine attempted to evaluate the nested expression repeatedly until no further substitution tags were found.

Remote Code Execution and Filter Evasion (CVE-2021-44228 and CVE-2021-45046)

Recursive evaluation significantly amplified the severity of the Log4Shell vulnerability:

Denial of Service via Infinite Recursion (CVE-2021-45105)

When applications configured non-default Pattern Layouts with Context Lookups in log4j2.xml, such as:

<PatternLayout pattern="%d %p %t %c $${ctx:loginId} %m%n" />

Log4j evaluated the $${ctx:loginId} directive dynamically during log formatting.

If an attacker submitted an input designed to reference itself recursively, such as:

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

or self-referencing variable chains like:

${ctx:loginId}

the StrSubstitutor class entered an uncontrolled recursive loop while attempting to resolve the nested tokens. Because there was no recursion depth limit, the Java Virtual Machine (JVM) exhausted its stack space, throwing a java.lang.StackOverflowError. This immediately crashed the logging thread or the entire application process, resulting in a remote Denial of Service.

Mitigation and Architectural Fixes

To remediate vulnerabilities arising from recursive lookups:

  1. Recursion Disablement: In Log4j 2.17.0 and later, recursive evaluation was completely removed from StrSubstitutor. Lookups are strictly evaluated in a non-recursive manner.
  2. Context Data Isolation: Only static, predefined lookup expressions are evaluated in configuration files; dynamically supplied user data from context maps cannot trigger nested lookup operations.
  3. Removal of JNDI Lookup Support: Support for the JNDI lookup protocol was disabled by default and restricted to prevent remote protocol invocations.