Elasticsearch часто оказывается «сердцем» логирования, поиска и аналитики. В него летят события приложений, логи nginx, audit trail, трейсинг, платежные события, пользовательские действия, debug-информация и иногда то, что вообще не должно было туда попасть.Материал предназначен только для обучения и легальной проверки собственных систем: учебных виртуальных машин, CTF-лабораторий, тестовых стендов и инфраструктуры, на аудит которой у вас есть письменное разрешение. Сканирование чужих сетей, просмотр чужих индексов, сбор данных и попытки доступа к не принадлежащим вам кластерам могут нарушать законодательство и правила провайдеров. Автор не несет ответственности за незаконное применение описанных подходов.
Именно поэтому открытый наружу Elasticsearch на 9200/tcp выглядит для защитника как красная лампа на панели: если кластер доступен без аутентификации, злоумышленник может увидеть названия индексов, схемы данных, объемы документов, а при худшей конфигурации еще и содержимое записей.
Ниже разберем базовую проверку: как искать свои хосты, как аккуратно посмотреть индексы и почему найденные данные почти всегда интереснее, чем кажется на первый взгляд.
1. Поиск хостов
Первый признак Elasticsearch по умолчанию — HTTP API на порту 9200. В старых или плохо настроенных инсталляциях этот порт мог оказаться доступен извне: из-за открытой security group, проброшенного Docker-порта, временного тестового стенда или «да потом закроем».Для проверки собственной инфраструктуры обычно используют несколько подходов:
- инвентаризация облака: security groups, firewall rules, load balancers;
- внутреннее сканирование разрешенных диапазонов;
- masscan или nmap по своим сетям;
- внешние поисковые сервисы вроде Shodan, Censys, FOFA, ZoomEye и похожих платформ, чтобы увидеть, что уже проиндексировано снаружи.
Пример команды для лабораторной сети:
masscan 192.168.56.0/24 -p9200 --rate 1000Где 192.168.56.0/24 — ваш учебный диапазон, например сеть VirtualBox/VMware/Proxmox.
Python: поиск Elasticsearch в учебной сети
Ниже пример простого скрипта для проверки своих адресов. Он не делает ничего «магического»: пытается подключиться к 9200, отправляет HTTP-запрос и смотрит, похож ли ответ на Elasticsearch.
Python:
#!/usr/bin/env python3
import ipaddress
import socket
import json
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
NETWORK = "192.168.56.0/24" # замените на свой учебный или корпоративный диапазон
PORT = 9200
TIMEOUT = 1.5
def is_port_open(host: str, port: int) -> bool:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.settimeout(TIMEOUT)
return sock.connect_ex((host, port)) == 0
def check_elasticsearch(host: str):
url = f"http://{host}:{PORT}/"
request = Request(url, headers={"User-Agent": "internal-es-audit/1.0"})
try:
with urlopen(request, timeout=TIMEOUT) as response:
body = response.read(4096).decode("utf-8", errors="replace")
data = json.loads(body)
if "cluster_name" in data or "tagline" in data:
return {
"host": host,
"url": url,
"cluster_name": data.get("cluster_name"),
"version": data.get("version", {}).get("number"),
"tagline": data.get("tagline"),
}
except (URLError, HTTPError, TimeoutError, json.JSONDecodeError):
return None
return None
def main():
network = ipaddress.ip_network(NETWORK, strict=False)
for ip in network.hosts():
host = str(ip)
if not is_port_open(host, PORT):
continue
result = check_elasticsearch(host)
if result:
print(json.dumps(result, ensure_ascii=False, indent=2))
else:
print(f"[?] {host}:{PORT} открыт, но ответ не похож на Elasticsearch")
if __name__ == "__main__":
main()
Пример ожидаемого вывода:
В реальной проверке полезно сохранять результаты в CSV/JSON, сопоставлять их с владельцами сервисов и сразу проверять, почему порт оказался доступен.{
"host": "192.168.56.12",
"url": "http://192.168.56.12:9200/",
"cluster_name": "docker-cluster",
"version": "8.12.0",
"tagline": "You Know, for Search"
}
2. Просмотр индексов
Если Elasticsearch отвечает, следующий безопасный шаг в рамках аудита — не читать документы, а посмотреть метаинформацию: список индексов, их размер, статус, количество документов. Этого часто достаточно, чтобы понять риск.Для ручной проверки в лаборатории можно использовать:
curl http://192.168.56.12:9200/_cat/indices?vЕсли включена аутентификация:
curl -u elastic:changeme http://192.168.56.12:9200/_cat/indices?vОбратите внимание на названия индексов. Даже без чтения документов они могут многое рассказать:
Названия вроде payments-prod, users, tokens, sessions, auth, crm, orders, passport, support-tickets уже говорят аудитору, что система требует немедленной проверки доступа.users-2026.07
payments-prod
nginx-access-logs
auth-events
crm-contacts
kibana_sample_data_ecommerce
Python: безопасный просмотр списка индексов
Этот скрипт запрашивает только список индексов через _cat/indices?format=json. Он не выгружает документы и не обращается к _search.
Python:
#!/usr/bin/env python3
import argparse
import json
from getpass import getpass
from urllib.request import Request, urlopen
from urllib.error import HTTPError, URLError
import base64
def build_auth_header(username: str | None, password: str | None):
if not username:
return {}
token = f"{username}:{password}".encode("utf-8")
encoded = base64.b64encode(token).decode("ascii")
return {"Authorization": f"Basic {encoded}"}
def fetch_indices(base_url: str, username: str | None, password: str | None):
url = base_url.rstrip("/") + "/_cat/indices?format=json&bytes=mb"
headers = {
"User-Agent": "internal-es-index-audit/1.0",
**build_auth_header(username, password),
}
request = Request(url, headers=headers)
with urlopen(request, timeout=5) as response:
return json.loads(response.read().decode("utf-8"))
def main():
parser = argparse.ArgumentParser(
description="List Elasticsearch indices for authorized security checks."
)
parser.add_argument("url", help="Example: http://192.168.56.12:9200")
parser.add_argument("-u", "--username", help="Elasticsearch username")
args = parser.parse_args()
password = getpass("Password: ") if args.username else None
try:
indices = fetch_indices(args.url, args.username, password)
print(f"{'health':<8} {'status':<8} {'index':<40} {'docs.count':>12} {'store.size':>12}")
print("-" * 88)
for item in indices:
print(
f"{item.get('health', ''):<8} "
f"{item.get('status', ''):<8} "
f"{item.get('index', ''):<40} "
f"{item.get('docs.count', ''):>12} "
f"{item.get('store.size', ''):>12}"
)
except HTTPError as error:
print(f"HTTP error: {error.code} {error.reason}")
except URLError as error:
print(f"Connection error: {error.reason}")
except json.JSONDecodeError:
print("Response was not valid JSON")
if __name__ == "__main__":
main()
Запуск без аутентификации в лаборатории:
python3 list_es_indices.py http://192.168.56.12:9200Запуск с пользователем:
python3 list_es_indices.py http://192.168.56.12:9200 -u elasticДля отчета по аудиту стоит фиксировать:
| Что проверять | Зачем |
Доступность 9200/tcp извне | Понять, виден ли Elasticsearch за пределами доверенной сети |
| Наличие аутентификации | Проверить, может ли посторонний получить метаданные |
| Названия индексов | Быстро оценить типы потенциально чувствительных данных |
| Размеры и количество документов | Оценить масштаб риска |
| Версию Elasticsearch | Понять, есть ли устаревшие компоненты и настройки |
3. Что бывает в индексах
Самое неприятное в открытом Elasticsearch — это не сам факт открытого порта. Неприятно то, что Elasticsearch редко бывает пустым. Обычно он существует потому, что туда уже долго и заботливо складывали данные.В индексах могут встречаться:
| Тип данных | Примеры |
| Логи приложений | stack traces, debug-сообщения, payload запросов |
| Учетные данные | email, username, иногда пароли или токены |
| Персональные данные | ФИО, телефоны, адреса, документы |
| Платежные события | заказы, суммы, статусы, masked/unmasked card data |
| Сессии и токены | JWT, refresh tokens, API keys |
| Логи инфраструктуры | IP-адреса, user-agent, internal hostnames |
| Данные CRM | клиенты, сделки, обращения в поддержку |
| Поисковые события | запросы пользователей, поведенческая аналитика |
| Служебные индексы | .kibana`, .security, мониторинг, ingest pipelines |
Особенно опасны debug-логи. Разработчик мог временно залогировать тело HTTP-запроса, заголовки авторизации или ответ внешнего API. Потом сервис ушел в прод, логирование осталось, а Elasticsearch стал архивом маленьких компромиссов.
Несколько крупных публичных историй
В открытых источниках описано много инцидентов, где проблема была не в «уязвимости Elasticsearch», а в незащищенной конфигурации: кластер был доступен из интернета без пароля, с открытым API или с ошибочными правилами firewall/security group. Ниже — несколько показательных случаев. Числа в таких публикациях обычно означают количество записей, а не обязательно количество уникальных людей.| Инцидент | Масштаб | Что было внутри | Почему это важно |
| CAM4, 2020 | 10,88 млрд записей | PII, email, IP-адреса, платежные логи, чаты, токены, password hashes | Один из самых крупных публично описанных случаев с misconfigured Elasticsearch; чувствительный контекст усиливал риск шантажа и фишинга. |
| Advanced Info Service / AIS, 2020 | 8,3 млрд записей | DNS query logs и NetFlow-логи пользователей тайского оператора | Даже «технические» сетевые логи могут раскрывать поведенческие паттерны пользователей. |
| Keepnet Labs, 2020 | более 5 млрд записей | Коллекция данных из прошлых утечек: email, пароли в разных форматах, источники и даты утечек | Ироничный пример: база с данными о прошлых инцидентах сама оказалась открыта во время миграции Elasticsearch. |
| Exactis, 2018 | около 340 млн записей | Маркетинговые профили: имена, адреса, телефоны, email, интересы, демография | Даже без номеров карт и SSN такая детализация удобна для социальной инженерии. |
| Microsoft Support, 2019/2020 | около 250 млн записей | Логи обращений в поддержку: email, IP, география, case numbers, описания заявок, внутренние пометки | Поддержка часто хранит контекст, который помогает мошенникам выглядеть убедительно. |
| Decathlon, 2020 | 123 млн записей | Данные клиентов и сотрудников, упоминавшиеся в публикациях о незащищенном Elasticsearch | Хороший пример того, что риск касается не только IT-компаний: ритейл, спорт, логистика и HR тоже часто складывают данные в поисковые индексы. |
| Китайский агрегированный датасет, 2025 | около 4 млрд записей | PII, финансовые данные, WeChat/Alipay-related коллекции, адресные и банковские наборы | Показывает масштаб риска, когда Elasticsearch используется как централизованная точка агрегации разнородных данных. |
Источники по примерам: WIRED о CAM4 (An Adult Cam Site Exposed 10.88 Billion Records), Security Magazine о крупнейших инцидентах 2020 года (The top 10 data breaches of 2020), WIRED об Exactis (Marketing Firm Leaked Database With 340 Million Records), PC Gamer о Microsoft Support (https://www.pcgamer.com/microsoft-d...abase-error-that-exposed-250-million-records/), TechRadar о причинах утечек Elasticsearch (https://www.techradar.com/news/what-is-elasticsearch-and-why-is-it-involved-in-so-many-data-leaks), TechRadar о китайском агрегированном датасете (https://www.techradar.com/pro/secur...rgest-breach-ever-heres-what-you-need-to-know).
Есть и отдельная категория атак не на чтение, а на уничтожение данных. В 2020 году прошла волна так называемых Meow-атак: открытые базы Elasticsearch, MongoDB и другие хранилища массово удалялись или портились автоматизированными скриптами. По данным SearchSecurity/TechTarget, атаки затронули более тысячи открытых баз, а позже счет шел уже на тысячи инцидентов; Elastic тогда указывала, что пострадали кластеры без включенных security-функций. Источник: TechTarget ('Meow' attacks wipe more than 1,000 exposed databases | TechTarget).
Похожая мораль встречается и в современных утечках: открытый поисковый кластер может стать не просто «окном для просмотра», а полноценной точкой массовой компрометации. В новостях регулярно появляются случаи, где через незащищенный Elasticsearch находили логины, пароли, документы, платежные данные или внутренние токены.
Почему названия индексов уже важны
Даже если аудитор не читает документы, одни только имена индексов могут подсказать, где пожар:Такой список уже отвечает на вопросы:prod-users
prod-orders
payment-events
auth-logs
support-messages
passport-verification
mobile-app-errors
- какие бизнес-процессы завязаны на кластер;
- есть ли там персональные или платежные данные;
- разделены ли dev/stage/prod;
- кто владелец данных;
- насколько срочно нужно закрывать доступ.
Что делать после обнаружения
Если в своей инфраструктуре найден Elasticsearch, доступный без должной защиты, порядок действий должен быть быстрым и спокойным:- Ограничить сетевой доступ: firewall, security groups, VPN, private subnet.
- Включить аутентификацию и TLS.
- Проверить роли и права пользователей.
- Изучить логи доступа к Elasticsearch и reverse proxy.
- Оценить, какие индексы могли быть доступны.
- Проверить наличие чувствительных данных в логах.
- При необходимости запустить процедуру incident response.
- Добавить постоянный контроль: cloud posture, external attack surface monitoring, регулярные сканы своих диапазонов.
Финальная мысль
Elasticsearch сам по себе не «дырявый». Проблема почти всегда в окружении: порт случайно открыт наружу, аутентификация выключена, тестовый контейнер стал продом, временное правило firewall пережило релиз.Хорошая проверка безопасности начинается не с громких эксплойтов, а с простого вопроса: «Кто вообще видит мой 9200?» Иногда ответ на него звучит неприятно: «Все».