AnalysisType: rule
Filename: aws_ssm_decrypt_ssm_params.py
RuleID: "AWS.SSM.DecryptSSMParams"
DisplayName: AWS Decrypt SSM Parameters
Enabled: true
LogTypes:
- AWS.CloudTrail
Severity: Medium
Reports:
MITRE ATT&CK:
- TA0006:T1555
Stratus Red Team:
- aws.credential-access.ssm-retrieve-securestring-parameters
Description: >
Identify principals retrieving a high number of SSM Parameters of type 'SecretString'.
This rule filters out known administrative roles that legitimately need bulk parameter access.
Threshold: 25
Reference: >
https://stratus-red-team.cloud/attack-techniques/AWS/aws.credential-access.ssm-retrieve-securestring-parameters/
Runbook: |
1. Query CloudTrail for all GetParameter and GetParameters events by userIdentity.arn with requestParameters.withDecryption=true in the 4 hours around this alert to identify the complete list of accessed SSM parameters
2. Review the parameter names from the resources array to determine if they contain database credentials, API keys, or encryption keys, and assess the impact if those secrets are compromised
3. Search CloudTrail for other suspicious API calls by the same userIdentity.arn and sourceIPAddress in the 24 hours before the first parameter access, looking for privilege escalation, IAM changes, or unusual resource access
SummaryAttributes:
- sourceIpAddress
- p_alert_context.accessedParams
Tags:
- AWS CloudTrail
- 'Credential Access: Credentials from Password Stores'
Status: Experimental
Tests:
- Name: Single Secret Accessed in Single Event
ExpectedResult: true
Mocks:
- objectName: get_string_set
returnValue: '["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y"]'
- objectName: put_string_set
returnValue: ''
Log:
{
"p_event_time": "2025-02-14 19:43:09.000000000",
"p_log_type": "AWS.CloudTrail",
"awsRegion": "us-west-2",
"eventCategory": "Management",
"eventID": "587e6d58-a653-4fd9-859f-367dc1bad98c",
"eventName": "GetParameter",
"eventSource": "ssm.amazonaws.com",
"eventTime": "2025-02-14 19:43:09.000000000",
"eventType": "AwsApiCall",
"eventVersion": "1.11",
"managementEvent": true,
"readOnly": true,
"recipientAccountId": "111122223333",
"requestID": "a1f28efd-9f5b-4a13-9878-86f57de594dc",
"requestParameters": {
"name": "/credentials/stratus-red-team/credentials-25",
"withDecryption": true
},
"resources": [
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-25"
}
],
"sourceIPAddress": "1.2.3.4",
"tlsDetails": {
"cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
"clientProvidedHostHeader": "ssm.us-west-2.amazonaws.com",
"tlsVersion": "TLSv1.2"
},
"userAgent": "example-user-agent",
"userIdentity": {
"accessKeyId": "EXAMPLE_ACCESS_KEY",
"accountId": "111122223333",
"arn": "arn:aws:sts::111122223333:assumed-role/SampleRole/bobson.dugnutt",
"principalId": "SAMPLE_PRINCIPAL_ID:bobson.dugnutt",
"sessionContext": {
"attributes": {
"creationDate": "2025-02-14T19:42:05Z",
"mfaAuthenticated": "false"
},
"sessionIssuer": {
"accountId": "111122223333",
"arn": "arn:aws:iam::111122223333:role/aws-reserved/sso.amazonaws.com/us-west-2/SampleRole",
"principalId": "SAMPLE_PRINCIPAL_ID",
"type": "Role",
"userName": "SampleRole"
}
},
"type": "AssumedRole"
}
}
- Name: Multiple Secrets Accessed in Same Event
ExpectedResult: true
Mocks:
- objectName: get_string_set
returnValue: '["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o"]'
- objectName: put_string_set
returnValue: ''
Log:
{
"p_event_time": "2025-02-14 19:42:57.000000000",
"p_log_type": "AWS.CloudTrail",
"awsRegion": "us-west-2",
"eventCategory": "Management",
"eventID": "ce59873d-6a27-4fa4-afc1-088fceba71e4",
"eventName": "GetParameters",
"eventSource": "ssm.amazonaws.com",
"eventTime": "2025-02-14 19:42:57.000000000",
"eventType": "AwsApiCall",
"eventVersion": "1.11",
"managementEvent": true,
"readOnly": true,
"recipientAccountId": "111122223333",
"requestID": "b6cb0ea5-2366-47c3-a4e5-acc31bc6882a",
"requestParameters": {
"names": [
"/credentials/stratus-red-team/credentials-10",
"/credentials/stratus-red-team/credentials-11",
"/credentials/stratus-red-team/credentials-12",
"/credentials/stratus-red-team/credentials-15",
"/credentials/stratus-red-team/credentials-24",
"/credentials/stratus-red-team/credentials-30",
"/credentials/stratus-red-team/credentials-31",
"/credentials/stratus-red-team/credentials-32",
"/credentials/stratus-red-team/credentials-36",
"/credentials/stratus-red-team/credentials-40",
"/credentials/stratus-red-team/credentials-41",
],
"withDecryption": true
},
"resources": [
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-10"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-11"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-12"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-15"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-24"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-30"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-31"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-32"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-36"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-40"
}
],
"sourceIPAddress": "1.2.3.4",
"tlsDetails": {
"cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
"clientProvidedHostHeader": "ssm.us-west-2.amazonaws.com",
"tlsVersion": "TLSv1.2"
},
"userAgent": "example-user-agent",
"userIdentity": {
"accessKeyId": "EXAMPLE_ACCESS_KEY",
"accountId": "111122223333",
"arn": "arn:aws:sts::111122223333:assumed-role/SampleRole/bobson.dugnutt",
"principalId": "SAMPLE_PRINCIPAL_ID:bobson.dugnutt",
"sessionContext": {
"attributes": {
"creationDate": "2025-02-14T19:42:05Z",
"mfaAuthenticated": "false"
},
"sessionIssuer": {
"accountId": "111122223333",
"arn": "arn:aws:iam::111122223333:role/aws-reserved/sso.amazonaws.com/us-west-2/SampleRole",
"principalId": "SAMPLE_PRINCIPAL_ID",
"type": "Role",
"userName": "SampleRole"
}
},
"type": "AssumedRole"
}
}
- Name: Multiple Secrets Accessed in Same Event With Prior Cached Parameters
ExpectedResult: true
Mocks:
- objectName: get_string_set
returnValue: '["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u"]'
- objectName: put_string_set
returnValue: ''
Log:
{
"p_event_time": "2025-02-14 19:42:57.000000000",
"p_log_type": "AWS.CloudTrail",
"awsRegion": "us-west-2",
"eventCategory": "Management",
"eventID": "ce59873d-6a27-4fa4-afc1-088fceba71e4",
"eventName": "GetParameters",
"eventSource": "ssm.amazonaws.com",
"eventTime": "2025-02-14 19:42:57.000000000",
"eventType": "AwsApiCall",
"eventVersion": "1.11",
"managementEvent": true,
"readOnly": true,
"recipientAccountId": "111122223333",
"requestID": "b6cb0ea5-2366-47c3-a4e5-acc31bc6882a",
"requestParameters": {
"names": [
"/credentials/stratus-red-team/credentials-10",
"/credentials/stratus-red-team/credentials-11",
"/credentials/stratus-red-team/credentials-12",
"/credentials/stratus-red-team/credentials-15",
"/credentials/stratus-red-team/credentials-24"
],
"withDecryption": true
},
"resources": [
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-10"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-11"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-12"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-15"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-24"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-30"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-31"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-32"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-36"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-40"
}
],
"sourceIPAddress": "1.2.3.4",
"tlsDetails": {
"cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
"clientProvidedHostHeader": "ssm.us-west-2.amazonaws.com",
"tlsVersion": "TLSv1.2"
},
"userAgent": "example-user-agent",
"userIdentity": {
"accessKeyId": "EXAMPLE_ACCESS_KEY",
"accountId": "111122223333",
"arn": "arn:aws:sts::111122223333:assumed-role/SampleRole/bobson.dugnutt",
"principalId": "SAMPLE_PRINCIPAL_ID:bobson.dugnutt",
"sessionContext": {
"attributes": {
"creationDate": "2025-02-14T19:42:05Z",
"mfaAuthenticated": "false"
},
"sessionIssuer": {
"accountId": "111122223333",
"arn": "arn:aws:iam::111122223333:role/aws-reserved/sso.amazonaws.com/us-west-2/SampleRole",
"principalId": "SAMPLE_PRINCIPAL_ID",
"type": "Role",
"userName": "SampleRole"
}
},
"type": "AssumedRole"
}
}
- Name: Accessed Parameters Aren't Encrypted
ExpectedResult: false
Mocks:
- objectName: get_string_set
returnValue: '[]'
- objectName: put_string_set
returnValue: ''
Log:
{
"p_event_time": "2025-02-14 19:42:57.000000000",
"p_log_type": "AWS.CloudTrail",
"awsRegion": "us-west-2",
"eventCategory": "Management",
"eventID": "ce59873d-6a27-4fa4-afc1-088fceba71e4",
"eventName": "GetParameters",
"eventSource": "ssm.amazonaws.com",
"eventTime": "2025-02-14 19:42:57.000000000",
"eventType": "AwsApiCall",
"eventVersion": "1.11",
"managementEvent": true,
"readOnly": true,
"recipientAccountId": "111122223333",
"requestID": "b6cb0ea5-2366-47c3-a4e5-acc31bc6882a",
"requestParameters": {
"names": [
"/credentials/stratus-red-team/credentials-10",
"/credentials/stratus-red-team/credentials-11",
"/credentials/stratus-red-team/credentials-12",
"/credentials/stratus-red-team/credentials-15",
"/credentials/stratus-red-team/credentials-24",
"/credentials/stratus-red-team/credentials-30",
"/credentials/stratus-red-team/credentials-31",
"/credentials/stratus-red-team/credentials-32",
"/credentials/stratus-red-team/credentials-36",
"/credentials/stratus-red-team/credentials-40",
"/credentials/stratus-red-team/credentials-41",
]
},
"resources": [
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-10"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-11"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-12"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-15"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-24"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-30"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-31"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-32"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-36"
},
{
"accountId": "111122223333",
"arn": "arn:aws:ssm:us-west-2:111122223333:parameter/credentials/stratus-red-team/credentials-40"
}
],
"sourceIPAddress": "1.2.3.4",
"tlsDetails": {
"cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
"clientProvidedHostHeader": "ssm.us-west-2.amazonaws.com",
"tlsVersion": "TLSv1.2"
},
"userAgent": "example-user-agent",
"userIdentity": {
"accessKeyId": "EXAMPLE_ACCESS_KEY",
"accountId": "111122223333",
"arn": "arn:aws:sts::111122223333:assumed-role/SampleRole/bobson.dugnutt",
"principalId": "SAMPLE_PRINCIPAL_ID:bobson.dugnutt",
"sessionContext": {
"attributes": {
"creationDate": "2025-02-14T19:42:05Z",
"mfaAuthenticated": "false"
},
"sessionIssuer": {
"accountId": "111122223333",
"arn": "arn:aws:iam::111122223333:role/aws-reserved/sso.amazonaws.com/us-west-2/SampleRole",
"principalId": "SAMPLE_PRINCIPAL_ID",
"type": "Role",
"userName": "SampleRole"
}
},
"type": "AssumedRole"
}
}
- Name: Unrelated Event
ExpectedResult: false
Log:
{
"awsRegion": "us-west-2",
"eventCategory": "Management",
"eventID": "6c6de06f-eb03-44cd-a95f-928a780ce28a",
"eventName": "DescribeParameters",
"eventSource": "ssm.amazonaws.com",
"eventTime": "2025-02-14 19:43:07.000000000",
"eventType": "AwsApiCall",
"eventVersion": "1.11",
"managementEvent": true,
"readOnly": true,
"recipientAccountId": "111122223333",
"requestID": "9ea104aa-d9af-415f-9c56-b7bb98c7c73f",
"requestParameters": {
"parameterFilters": [
{
"key": "Name",
"option": "Equals",
"values": [
"/credentials/stratus-red-team/credentials-1"
]
}
]
},
"sourceIPAddress": "1.2.3.4",
"tlsDetails": {
"cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
"clientProvidedHostHeader": "ssm.us-west-2.amazonaws.com",
"tlsVersion": "TLSv1.2"
},
"userAgent": "example-user-agent",
"userIdentity": {
"accessKeyId": "EXAMPLE_ACCESS_KEY",
"accountId": "111122223333",
"arn": "arn:aws:sts::111122223333:assumed-role/SampleRole/bobson.dugnutt",
"principalId": "SAMPLE_PRINCIPAL_ID:bobson.dugnutt",
"sessionContext": {
"attributes": {
"creationDate": "2025-02-14T19:42:05Z",
"mfaAuthenticated": "false"
},
"sessionIssuer": {
"accountId": "111122223333",
"arn": "arn:aws:iam::111122223333:role/aws-reserved/sso.amazonaws.com/us-west-2/SampleRole",
"principalId": "SAMPLE_PRINCIPAL_ID",
"type": "Role",
"userName": "SampleRole"
}
},
"type": "AssumedRole"
}
}
# ------ paired body: aws_ssm_decrypt_ssm_params.py ------
import datetime as dt
import json
from panther_aws_helpers import aws_cloudtrail_success, aws_rule_context
from panther_core import PantherEvent
from panther_detection_helpers.caching import get_string_set, put_string_set
# Determine how many secrets must be accessed in order to trigger an alert
PARAM_THRESHOLD = 25
# Whitelisted IAM role name patterns (case-insensitive)
WHITELISTED_ROLE_PATTERNS = [
"AWSReservedSSO_DevAdmin_", # SSO admin roles
"AWSReservedSSO_Admin_", # SSO admin roles
]
all_param_names = set()
def rule(event: PantherEvent) -> bool:
# Exclude events of the wrong type
if not (
event.get("eventName") in ("GetParameter", "GetParameters")
and event.deep_get("requestParameters", "withDecryption")
):
return False
# Check if the role is whitelisted
role_name = event.deep_get(
"userIdentity", "sessionContext", "sessionIssuer", "userName", default=""
)
if role_name and any(
pattern.lower() in role_name.lower() for pattern in WHITELISTED_ROLE_PATTERNS
):
return False
# Determine if this actor accessed any other params in this account
key = get_cache_key(event)
cached_params = get_cached_param_names(key)
accessed_params = get_param_names(event)
# Determine if the cache needs updating with new entries
global all_param_names # pylint: disable=global-statement
all_param_names = cached_params | accessed_params
if all_param_names - cached_params:
# Only set the TTL if this is the first time we're adding to the cache
# Otherwise we'll be perpetually extending the lifespan of the cached data every time we
# add more.
put_string_set(key, all_param_names, epoch_seconds=(3600 if not cached_params else None))
# Check combined number of params
return len(all_param_names) > PARAM_THRESHOLD
def title(event: PantherEvent) -> str:
actor = event.udm("actor_user")
account_name = event.get("recipientAccountId")
return f"Excessive SSM parameter decryption by [{actor}] in [{account_name}]"
def severity(event: PantherEvent) -> str:
# Demote to LOW if attempt was denied
if not aws_cloudtrail_success(event):
return "LOW"
return "DEFAULT"
def alert_context(event: PantherEvent) -> dict:
global all_param_names
context = aws_rule_context(event)
context.update({"accessedParams": list(all_param_names)})
return context
def get_cache_key(event) -> str:
"""Use the field values in the event to generate a cache key unique to this actor and
account ID."""
offset = (
dt.datetime.fromisoformat(event.get("p_event_time", "1970-01-01T00:00:00")).timestamp()
// 3600
* 3600
)
actor = event.udm("actor_user")
account = event.get("recipientAccountId")
rule_id = "AWS.SSM.DecryptSSMParams"
return f"{rule_id}-{account}-{actor}-{offset}"
def get_param_names(event) -> set[str]:
"""Returns the accessed SSM Param names."""
# Params could be either a list or a single entry
params = set(event.deep_get("requestParameters", "names", default=[]))
if single_param := event.deep_get("requestParameters", "name"):
params.add(single_param)
return params
def get_cached_param_names(key: str) -> set[str]:
"""Get any previously cached parameter names. Included automatic converstion from string in
the case of a unit test mock."""
cached_params = get_string_set(key, force_ttl_check=True)
if isinstance(cached_params, str):
# This is a unit test
cached_params = set(json.loads(cached_params))
return cached_params