Detection rules › Kusto

AWSCloudTrail - Changes to AWS Security Group ingress and egress settings

Status
available
Severity
low
Time window
1d
Group by
AWSRegion, AccountName, AccountUPNSuffix, AdditionalEventData, EventName, EventSource, RecipientAccountId, ResponseElements, SessionMfaAuthenticated, SourceIpAddress, UserAgent, UserIdentityAccountId, UserIdentityPrincipalid, UserIdentityType
Source
github.com/Azure/Azure-Sentinel

A Security Group acts as a virtual firewall for an AWS instance to control inbound and outbound traffic. This rule detects AWS CloudTrail events for changes to Security Group ingress and egress settings. Investigate to validate the legitimacy of the activity and identify potential malicious activity.

MITRE ATT&CK coverage

Telemetry coverage

Rules detecting the same action

These rules filter on the same operation.

Rule body

id: 4f19d4e3-ec5f-4abc-9e61-819eb131758c
name: AWSCloudTrail - Changes to AWS Security Group ingress and egress settings
description: |
  A Security Group acts as a virtual firewall for an AWS instance to control inbound and outbound traffic. This rule detects AWS CloudTrail events for changes to Security Group ingress and egress settings.
  Investigate to validate the legitimacy of the activity and identify potential malicious activity.
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: AWS
    dataTypes:
      - AWSCloudTrail
  - connectorId: AWSS3
    dataTypes:
      - AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1562.007
query: |
  let EventNameList = dynamic([ "AuthorizeSecurityGroupEgress", "AuthorizeSecurityGroupIngress", "RevokeSecurityGroupEgress", "RevokeSecurityGroupIngress"]);
  AWSCloudTrail
  | where EventName in~ (EventNameList)
  | extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
  | extend UserName = tostring(split(UserIdentityArn, '/')[-1])
  | extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
  | extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
    AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
  | summarize EventCount=count(), StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated)
  by EventSource, EventName, UserIdentityType, RecipientAccountId, AccountName, AccountUPNSuffix, SourceIpAddress, UserAgent, SessionMfaAuthenticated, AWSRegion,
  AdditionalEventData, UserIdentityAccountId, UserIdentityPrincipalid, ResponseElements
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
      - identifier: CloudAppAccountId
        columnName: RecipientAccountId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIpAddress
customDetails:
  UserIdentityArn: UserIdentityArn
  UserIdentityUserName: UserIdentityUserName
  UserIdentityType: UserIdentityType
  EventName: EventName
  AWSRegion: AWSRegion
  UserAgent: UserAgent
alertDetailsOverride:
  alertDisplayNameFormat: 'AWS Security Group ingress/egress change by {{AccountName}} from {{SourceIpAddress}}'
  alertDescriptionFormat: 'AWS Security Group ingress/egress change activity {{EventName}} was detected for {{AccountName}} from {{SourceIpAddress}}.' 
version: 1.0.5
kind: Scheduled

Stages and Predicates

Parameters

let EventNameList = dynamic([ "AuthorizeSecurityGroupEgress", "AuthorizeSecurityGroupIngress", "RevokeSecurityGroupEgress", "RevokeSecurityGroupIngress"]);

Stage 1: source

AWSCloudTrail

Stage 2: where

| where EventName in~ (EventNameList)

Stage 3: extend (4 consecutive steps)

| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
  AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")

Stage 4: summarize

| summarize EventCount=count(), StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated)
by EventSource, EventName, UserIdentityType, RecipientAccountId, AccountName, AccountUPNSuffix, SourceIpAddress, UserAgent, SessionMfaAuthenticated, AWSRegion,
AdditionalEventData, UserIdentityAccountId, UserIdentityPrincipalid, ResponseElements

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventNamein
  • AuthorizeSecurityGroupEgress
  • AuthorizeSecurityGroupIngress
  • RevokeSecurityGroupEgress
  • RevokeSecurityGroupIngress
field:"aws::eventName" kind:in

Output fields

These fields are emitted when the rule matches.

FieldSource
AWSRegionsummarize
AccountNamesummarize
AccountUPNSuffixsummarize
AdditionalEventDatasummarize
EndTimeUtcsummarize
EventCountsummarize
EventNamesummarize
EventSourcesummarize
RecipientAccountIdsummarize
ResponseElementssummarize
SessionMfaAuthenticatedsummarize
SourceIpAddresssummarize
StartTimeUtcsummarize
UserAgentsummarize
UserIdentityAccountIdsummarize
UserIdentityPrincipalidsummarize
UserIdentityTypesummarize