Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
[[prebuilt-rule-8-19-4-aws-access-token-used-from-multiple-addresses]]
=== AWS Access Token Used from Multiple Addresses

This rule identifies potentially suspicious activity by detecting instances where a single IAM user's temporary session token is accessed from multiple IP addresses within a short time frame. Such behavior may suggest that an adversary has compromised temporary credentials and is utilizing them from various locations. To enhance detection accuracy and minimize false positives, the rule incorporates criteria that evaluate unique IP addresses, user agents, cities, and networks. These additional checks help distinguish between legitimate distributed access patterns and potential credential misuse. Detected activities are classified into different types based on the combination of unique indicators, with each classification assigned a fidelity score reflecting the likelihood of malicious behavior. High fidelity scores are given to patterns most indicative of threats, such as multiple unique IPs, networks, cities, and user agents. Medium and low fidelity scores correspond to less severe patterns, enabling security teams to effectively prioritize alerts.

*Rule type*: esql

*Rule indices*: None

*Severity*: medium

*Risk score*: 47

*Runs every*: 5m

*Searches indices from*: now-32m ({ref}/common-options.html#date-math[Date Math format], see also <<rule-schedule, `Additional look-back time`>>)

*Maximum alerts per execution*: 100

*References*:

* https://www.sygnia.co/blog/sygnia-investigation-bybit-hack/

*Tags*:

* Domain: Cloud
* Data Source: AWS
* Data Source: Amazon Web Services
* Data Source: AWS IAM
* Data Source: AWS CloudTrail
* Tactic: Initial Access
* Use Case: Identity and Access Audit
* Resources: Investigation Guide

*Version*: 102

*Rule authors*:

* Elastic

*Rule license*: Elastic License v2


==== Investigation guide



*Triage and Analysis*



*Investigating AWS Access Token Used from Multiple Addresses*


Access tokens are bound to a single user. Usage from multiple IP addresses may indicate the token was stolen and used elsewhere. By correlating this with additional detection criteria like multiple user agents, different cities, and different networks, we can improve the fidelity of the rule and help to eliminate false positives associated with expected behavior, like dual-stack IPV4/IPV6 usage.


*Possible Investigation Steps*


- **Identify the IAM User**: Examine the `aws.cloudtrail.user_identity.arn` stored in `user_id` and correlate with the `source.ips` stored in `ip_list` and `unique_ips` count to determine how widely the token was used.
- **Correlate Additional Detection Context**: Examine `activity_type` and `fidelity_score` to determine additional cities, networks or user agents associated with the token usage.
- **Determine Access Key Type**: Examine the `access_key_id` to determine whether the token is short-term (beginning with ASIA) or long-term (beginning with AKIA).
- **Check Recent MFA Events**: Determine whether the user recently enabled MFA, registered devices, or assumed a role using this token.
- **Review Workload Context**: Confirm whether the user was expected to be active across multiple cities, networks or user agent environments.
- **Trace Adversary Movement**: Pivot to related actions (e.g., `s3:ListBuckets`, `iam:ListUsers`, `sts:GetCallerIdentity`) to track further enumeration.


*False Positive Analysis*


- Automation frameworks that rotate through multiple IPs or cloud functions with dynamic egress IPs may cause this alert to fire.
- Confirm geolocation and workload context before escalating.


*Response and Remediation*


- **Revoke the Token**: Disable or rotate the IAM credentials and invalidate the temporary session token.
- **Audit the Environment**: Look for signs of lateral movement or data access during the token's validity.
- **Strengthen Controls**: Require MFA for high-privilege actions, restrict access via policy conditions (e.g., IP range or device).


*References*


- https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html[IAM Long-Term Credentials]
- https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html[STS Temporary Credentials]
- https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html[Using MFA with Temporary Credentials]
- https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html[AWS Threat Detection Use Cases]


==== Rule query


[source, js]
----------------------------------
from logs-aws.cloudtrail* metadata _id, _version, _index
| where @timestamp > now() - 30 minutes
and event.dataset == "aws.cloudtrail"
and aws.cloudtrail.user_identity.arn is not null
and aws.cloudtrail.user_identity.type == "IAMUser"
and source.ip is not null
and not (
user_agent.original like "%Terraform%" or
user_agent.original like "%Ansible%" or
user_agent.original like "%Pulumni%"
)
and `source.as.organization.name` != "AMAZON-AES"
and event.provider not in (
"health.amazonaws.com", "monitoring.amazonaws.com", "notifications.amazonaws.com",
"ce.amazonaws.com", "cost-optimization-hub.amazonaws.com",
"servicecatalog-appregistry.amazonaws.com", "securityhub.amazonaws.com"
)

| eval
Esql.time_window_date_trunc = date_trunc(30 minutes, @timestamp),
Esql.aws_cloudtrail_user_identity_arn = aws.cloudtrail.user_identity.arn,
Esql.aws_cloudtrail_user_identity_access_key_id = aws.cloudtrail.user_identity.access_key_id,
Esql.source_ip = source.ip,
Esql.user_agent_original = user_agent.original,
Esql.source_ip_string = to_string(source.ip),
Esql.source_ip_user_agent_pair = concat(Esql.source_ip_string, " - ", user_agent.original),
Esql.source_ip_city_pair = concat(Esql.source_ip_string, " - ", source.geo.city_name),
Esql.source_geo_city_name = source.geo.city_name,
Esql.event_timestamp = @timestamp,
Esql.source_network_org_name = `source.as.organization.name`

| stats
Esql.event_action_values = values(event.action),
Esql.event_provider_values = values(event.provider),
Esql.aws_cloudtrail_user_identity_access_key_id_values = values(Esql.aws_cloudtrail_user_identity_access_key_id),
Esql.aws_cloudtrail_user_identity_arn_values = values(Esql.aws_cloudtrail_user_identity_arn),
Esql.source_ip_values = values(Esql.source_ip),
Esql.user_agent_original_values = values(Esql.user_agent_original),
Esql.source_ip_user_agent_pair_values = values(Esql.source_ip_user_agent_pair),
Esql.source_geo_city_name_values = values(Esql.source_geo_city_name),
Esql.source_ip_city_pair_values = values(Esql.source_ip_city_pair),
Esql.source_network_org_name_values = values(Esql.source_network_org_name),
Esql.source_ip_count_distinct = count_distinct(Esql.source_ip),
Esql.user_agent_original_count_distinct = count_distinct(Esql.user_agent_original),
Esql.source_geo_city_name_count_distinct = count_distinct(Esql.source_geo_city_name),
Esql.source_network_org_name_count_distinct = count_distinct(Esql.source_network_org_name),
Esql.timestamp_first_seen = min(Esql.event_timestamp),
Esql.timestamp_last_seen = max(Esql.event_timestamp),
Esql.event_count = count()
by Esql.time_window_date_trunc, Esql.aws_cloudtrail_user_identity_access_key_id

| eval
Esql.activity_type = case(
Esql.source_ip_count_distinct >= 2 and Esql.source_network_org_name_count_distinct >= 2 and Esql.source_geo_city_name_count_distinct >= 2 and Esql.user_agent_original_count_distinct >= 2, "multiple_ip_network_city_user_agent",
Esql.source_ip_count_distinct >= 2 and Esql.source_network_org_name_count_distinct >= 2 and Esql.source_geo_city_name_count_distinct >= 2, "multiple_ip_network_city",
Esql.source_ip_count_distinct >= 2 and Esql.source_geo_city_name_count_distinct >= 2, "multiple_ip_and_city",
Esql.source_ip_count_distinct >= 2 and Esql.source_network_org_name_count_distinct >= 2, "multiple_ip_and_network",
Esql.source_ip_count_distinct >= 2 and Esql.user_agent_original_count_distinct >= 2, "multiple_ip_and_user_agent",
"normal_activity"
),
Esql.activity_fidelity_score = case(
Esql.activity_type == "multiple_ip_network_city_user_agent", "high",
Esql.activity_type == "multiple_ip_network_city", "high",
Esql.activity_type == "multiple_ip_and_city", "medium",
Esql.activity_type == "multiple_ip_and_network", "medium",
Esql.activity_type == "multiple_ip_and_user_agent", "low"
)

| keep
Esql.time_window_date_trunc,
Esql.activity_type,
Esql.activity_fidelity_score,
Esql.event_count,
Esql.timestamp_first_seen,
Esql.timestamp_last_seen,
Esql.aws_cloudtrail_user_identity_arn_values,
Esql.aws_cloudtrail_user_identity_access_key_id_values,
Esql.event_action_values,
Esql.event_provider_values,
Esql.source_ip_values,
Esql.user_agent_original_values,
Esql.source_ip_user_agent_pair_values,
Esql.source_geo_city_name_values,
Esql.source_ip_city_pair_values,
Esql.source_network_org_name_values,
Esql.source_ip_count_distinct,
Esql.user_agent_original_count_distinct,
Esql.source_geo_city_name_count_distinct,
Esql.source_network_org_name_count_distinct

| where Esql.activity_type != "normal_activity"

----------------------------------

*Framework*: MITRE ATT&CK^TM^

* Tactic:
** Name: Initial Access
** ID: TA0001
** Reference URL: https://attack.mitre.org/tactics/TA0001/
* Technique:
** Name: Valid Accounts
** ID: T1078
** Reference URL: https://attack.mitre.org/techniques/T1078/
* Sub-technique:
** Name: Cloud Accounts
** ID: T1078.004
** Reference URL: https://attack.mitre.org/techniques/T1078/004/
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
[[prebuilt-rule-8-19-4-aws-bedrock-detected-multiple-attempts-to-use-denied-models-by-a-single-user]]
=== AWS Bedrock Detected Multiple Attempts to use Denied Models by a Single User

Identifies multiple successive failed attempts to use denied model resources within AWS Bedrock. This could indicated attempts to bypass limitations of other approved models, or to force an impact on the environment by incurring exhorbitant costs.

*Rule type*: esql

*Rule indices*: None

*Severity*: high

*Risk score*: 73

*Runs every*: 10m

*Searches indices from*: now-60m ({ref}/common-options.html#date-math[Date Math format], see also <<rule-schedule, `Additional look-back time`>>)

*Maximum alerts per execution*: 100

*References*:

* https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails-components.html
* https://atlas.mitre.org/techniques/AML.T0015
* https://atlas.mitre.org/techniques/AML.T0034
* https://www.elastic.co/security-labs/elastic-advances-llm-security

*Tags*:

* Domain: LLM
* Data Source: AWS Bedrock
* Data Source: AWS S3
* Resources: Investigation Guide
* Use Case: Policy Violation
* Mitre Atlas: T0015
* Mitre Atlas: T0034

*Version*: 5

*Rule authors*:

* Elastic

*Rule license*: Elastic License v2


==== Investigation guide



*Triage and analysis*



*Investigating AWS Bedrock Detected Multiple Attempts to use Denied Models by a Single User*


Amazon Bedrock is AWS’s managed service that enables developers to build and scale generative AI applications using large foundation models (FMs) from top providers.

Bedrock offers a variety of pretrained models from Amazon (such as the Titan series), as well as models from providers like Anthropic, Meta, Cohere, and AI21 Labs.


*Possible investigation steps*


- Identify the user account that attempted to use denied models.
- Investigate other alerts associated with the user account during the past 48 hours.
- Consider the time of day. If the user is a human (not a program or script), did the activity take place during a normal time of day?
- Examine the account's attempts to access Amazon Bedrock models in the last 24 hours.
- If you suspect the account has been compromised, scope potentially compromised assets by tracking Amazon Bedrock model access, prompts generated, and responses to the prompts by the account in the last 24 hours.


*False positive analysis*


- Verify the user account that attempted to use denied models, is a legitimate misunderstanding by users or overly strict policies.


*Response and remediation*


- Initiate the incident response process based on the outcome of the triage.
- Disable or limit the account during the investigation and response.
- Identify the possible impact of the incident and prioritize accordingly; the following actions can help you gain context:
- Identify the account role in the cloud environment.
- Identify if the attacker is moving laterally and compromising other Amazon Bedrock Services.
- Identify any regulatory or legal ramifications related to this activity.
- Review the permissions assigned to the implicated user group or role behind these requests to ensure they are authorized and expected to access bedrock and ensure that the least privilege principle is being followed.
- Determine the initial vector abused by the attacker and take action to prevent reinfection via the same vector.
- Using the incident response data, update logging and audit policies to improve the mean time to detect (MTTD) and the mean time to respond (MTTR).


==== Setup



*Setup*


This rule requires that guardrails are configured in AWS Bedrock. For more information, see the AWS Bedrock documentation:

https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails-create.html


==== Rule query


[source, js]
----------------------------------
from logs-aws_bedrock.invocation-*

// Filter for access denied errors from GenAI responses
| where gen_ai.response.error_code == "AccessDeniedException"

// keep ECS and response fields
| keep
user.id,
gen_ai.request.model.id,
cloud.account.id,
gen_ai.response.error_code

// count total denials per user/model/account
| stats
Esql.ml_response_access_denied_count = count(*)
by
user.id,
gen_ai.request.model.id,
cloud.account.id

// Filter for users with repeated denials
| where Esql.ml_response_access_denied_count > 3

// sort by volume of denials
| sort Esql.ml_response_access_denied_count desc

----------------------------------
Loading