What Was Log4Shell? The Logger That Ran What It Was Told to Record

The payload was a string. The thing that ran it was the logger.

Not a parser, not a template engine, not an interpreter anybody had exposed on purpose. The library whose entire job is to write down what happened.

What was Log4Shell?

Log4Shell — CVE-2021-44228 — was an unauthenticated remote code execution vulnerability in Apache Log4j 2, versions 2.0-beta9 through 2.14.1. It went public on 9 December 2021 and scored a full 10.0.

Log4j is not a product anyone buys. It is the default logging library for an enormous amount of Java, which is to say an enormous amount of enterprise software, embedded systems, network appliances, and cloud services that nobody thinks of as Java at all. That is what made the response so ugly: most affected organisations could not answer the question do we run Log4j without weeks of work.

The mechanism

Log4j 2 supported lookups — small substitutions resolved inside a message as it is logged. Write a placeholder into a log line and Log4j replaces it with a value: an environment variable, a system property, a date.

One of the supported lookups was JNDI, the Java Naming and Directory Interface, which fetches an object from a directory service. Give it an LDAP address and it will connect out, retrieve what is there, and hand back a Java object.

Message lookups were on by default. So a string containing a JNDI lookup, appearing anywhere in text that Log4j eventually wrote to a log, caused the server to make an outbound connection to an address of the sender's choosing and load what it found.

No authentication. No prior access. No malformed request — the input was, in protocol terms, perfectly ordinary text.

“Anywhere” meant anywhere

This is the part that made the week what it was. The attack surface was not an endpoint. It was every field, in every application, that ever ends up in a log line.

  • An HTTP User-Agent header.
  • A username on a failed login — failures get logged by definition.
  • A Minecraft chat message. Servers logged chat, so a player could type it into the game.
  • The name of an iPhone. Researchers demonstrated that renaming a handset caused callbacks from servers that logged the device name.
  • Form fields, search queries, referrer headers, filenames, SIP headers, email subjects.

There was no single place to put a filter, because the vulnerable function was called from everywhere by design.

Correcting the record: the primitive was five years old

Log4Shell is remembered as a bolt from nowhere. The JNDI half of it was not new, and this is the detail most retellings leave out.

At Black Hat USA 2016, Alvaro Muñoz and Oleksandr Mirosh presented A Journey From JNDI/LDAP Manipulation to Remote Code Execution Dream Land — a thorough public treatment of exactly this: if an attacker controls the string handed to a JNDI lookup, they get code execution. Five years before Log4Shell, in the most public venue the field has.

What was missing in 2016 was a delivery mechanism sitting inside almost every Java application on earth. Log4j's message lookups supplied that.

The lesson is not “nobody saw it coming.” It is that a known-dangerous primitive and a widely-deployed component can sit apart from each other for years, and the day somebody connects them the severity is already maximum.

The other correction: newer Java was not safe

A claim that circulated hard in the first days was that recent JDKs were immune, because since 8u191 and 11.0.1 the property com.sun.jndi.ldap.object.trustURLCodebase defaults to false, which blocks loading a class from a remote codebase.

That is true, and it blocks the simplest path. It does not make you safe. Attackers moved to gadget chains already present in the target's own classpath — deserialisation of objects the application already knows how to build — and to information disclosure, exfiltrating environment variables and credentials through the DNS or LDAP lookup itself, which needs no class loading at all.

Plenty of teams read “we are on Java 11” and stood down. They were wrong, and some of them found out later.

Four releases to actually fix it

The patch history is a decent illustration of how hard it is to remove a feature under pressure.

  • 2.15.0 — shipped with the disclosure. Restricted lookups, but incompletely. Became CVE-2021-45046.
  • 2.16.0 — removed message lookup substitution entirely and disabled JNDI by default. The real fix.
  • 2.17.0 — addressed CVE-2021-45105, a denial of service through recursive lookups.
  • 2.17.1 — addressed CVE-2021-44832, a further JNDI issue reachable by an attacker who could modify configuration.

Four releases inside a month, over Christmas, while every organisation in the world was asking its vendors for a straight answer.

It was reported responsibly to Apache by Chen Zhaojun of the Alibaba Cloud Security Team on 24 November 2021. CISA issued Emergency Directive 22-02 on 17 December, requiring federal agencies to find and fix it.

What is still true

The durable output of Log4Shell was not a patch. It was that “what are we actually running” became a question organisations were expected to be able to answer, and that software bills of materials went from a compliance curiosity to something people genuinely wanted.

Vulnerable versions are still reachable on the internet today, in appliances and embedded systems whose vendors are gone.

Further reading

  • Apache — the Log4j Security Vulnerabilities page. The primary source for the version chain.
  • Muñoz and Mirosh — A Journey From JNDI/LDAP Manipulation to Remote Code Execution Dream Land (Black Hat USA 2016).
  • CISA Emergency Directive 22-02 and the joint advisory on Log4Shell.
  • The Cyber Safety Review Board's first report (July 2022), which took Log4Shell as its subject and called it an endemic vulnerability.

EXHIBIT 0009 — LOG4SHELL

We put it on a shirt. An open ledger of log entries, every line an illegible tick-mark except one — and out of that one line, a mechanical arm reaching off the page to pull something back in.

Most people will read it as an old book. A smaller number will read the one legible line.

That's the whole idea.

View the Log4Shell shirt — EXHIBIT 0009

Back to blog

Leave a comment

Please note, comments need to be approved before they are published.