id: 8e15998e-1e32-4b6d-abd1-e8482e8f3def name: AWSCloudTrail - Creation of CRUD KMS policy and then privilege escalation description: | Identifies creation of IAM policies that grant permissions for broad KMS CRUD actions, followed by attachment activity to a principal. This sequence can be used to expand access to encryption keys or disable protections and should be investigated as possible privilege escalation. severity: Medium status: Available requiredDataConnectors: - connectorId: AWS dataTypes: - AWSCloudTrail queryFrequency: 1d queryPeriod: 1d triggerOperator: gt triggerThreshold: 0 tactics: - DefenseEvasion - PrivilegeEscalation - Persistence relevantTechniques: - T1484 - T1098.003 query: | let EventNameList = dynamic(["AttachUserPolicy","AttachRolePolicy","AttachGroupPolicy"]); let createPolicy = dynamic(["CreatePolicy", "CreatePolicyVersion"]); let timeframe = 1d; let lookback = 14d; // Creating Master table with all the events to use with materialize for better performance let EventInfo = AWSCloudTrail | where TimeGenerated >= ago(lookback) | where EventName in (EventNameList) or EventName in (createPolicy) | 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]), ""); //Checking for Policy creation event with Full Admin Privileges since lookback period. let FullAdminPolicyEvents = materialize( EventInfo | where TimeGenerated >= ago(lookback) | where EventName in (createPolicy) | extend PolicyName = tostring(parse_json(RequestParameters).policyName) | extend Statement = parse_json(tostring((parse_json(RequestParameters).policyDocument))).Statement | mvexpand Statement | extend Action = parse_json(Statement).Action , Effect = tostring(parse_json(Statement).Effect), Resource = tostring(parse_json(Statement).Resource), Condition = tostring(parse_json(Statement).Condition) | extend Action = tostring(Action) | where Effect =~ "Allow" and (Action contains "kms:Create" and (Action contains "kms:Get" or Action contains "kms:Describe") and (Action contains "kms:Disable" or Action contains "kms:Enable") and Action contains "kms:Delete") and Resource == "*" and Condition == "" | distinct TimeGenerated, EventName, PolicyName, SourceIpAddress, UserIdentityArn, RecipientAccountId, AccountName, AccountUPNSuffix | project-rename StartTime = TimeGenerated ); let PolicyAttach = materialize( EventInfo | where TimeGenerated >= ago(timeframe) | where EventName in (EventNameList) and isempty(ErrorCode) and isempty(ErrorMessage) | extend PolicyName = tostring(split(tostring(parse_json(RequestParameters).policyArn),"/")[1]) | summarize AttachEventCount=count(), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by EventSource, EventName, UserIdentityType , UserIdentityArn, SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, PolicyName | extend AttachEvent = pack("StartTime", StartTime, "EndTime", EndTime, "EventName", EventName, "UserIdentityType", UserIdentityType, "SourceIpAddress", SourceIpAddress, "AccountName", AccountName, "AccountUPNSuffix", AccountUPNSuffix, "RecipientAccountId", RecipientAccountId, "UserIdentityArn", UserIdentityArn) | project EventSource, PolicyName, AttachEvent, RecipientAccountId, AccountName, AccountUPNSuffix, AttachEventCount ); // Joining the list of PolicyNames and checking if it has been attached to any Roles/Users/Groups. // These Roles/Users/Groups will be Privileged and can be used by adversaries as pivot point for privilege escalation via multiple ways. FullAdminPolicyEvents | join kind=leftouter ( PolicyAttach ) on PolicyName | project-away PolicyName1 entityMappings: - entityType: Account fieldMappings: - identifier: Name columnName: AccountName - identifier: UPNSuffix columnName: AccountUPNSuffix - identifier: CloudAppAccountId columnName: RecipientAccountId - entityType: IP fieldMappings: - identifier: Address columnName: SourceIpAddress customDetails: PolicyName: PolicyName EventName: EventName RecipientAccountId: RecipientAccountId UserIdentityArn: UserIdentityArn alertDetailsOverride: alertDisplayNameFormat: 'AWS KMS policy {{PolicyName}} created and attached by {{AccountName}}' alertDescriptionFormat: 'Detected {{EventName}} for policy {{PolicyName}} in account {{RecipientAccountId}}.' version: 1.0.3 kind: Scheduled