-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8808 from Azure/Multi_Cloud_Detection
New Detection for Multi Cloud
- Loading branch information
Showing
9 changed files
with
900 additions
and
0 deletions.
There are no files selected for viewing
102 changes: 102 additions & 0 deletions
102
Detections/MultipleDataSources/BrutforceAttemptOnAzurePortalAndAWSConsolAtSameTime.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
id: 1f40ed57-f54b-462f-906a-ac3a89cc90d4 | ||
name: Cross-Cloud Password Spray detection | ||
description: | | ||
'This detection focuses on identifying potential cross-cloud brute force / Password Spray attempts involving Azure and AWS platforms. It monitors sign-in activities within the Azure Portal and AWS ConsoleLogins where brute force attempts are successful on both platforms in a synchronized manner.' | ||
severity: Medium | ||
requiredDataConnectors: | ||
- connectorId: AWS | ||
dataTypes: | ||
- AWSCloudTrail | ||
- connectorId: AzureActiveDirectory | ||
dataTypes: | ||
- SigninLogs | ||
- connectorId: BehaviorAnalytics | ||
dataTypes: | ||
- IdentityInfo | ||
- connectorId: MicrosoftThreatProtection | ||
dataTypes: | ||
- SecurityAlert | ||
queryFrequency: 1d | ||
queryPeriod: 1d | ||
triggerOperator: gt | ||
triggerThreshold: 0 | ||
tactics: | ||
- CredentialAccess | ||
relevantTechniques: | ||
- T1110 | ||
query: | | ||
// Materialize a table named "Azure_Bruforce" containing Azure Portal sign-in logs within the last 1 day | ||
let Azure_Bruforce = materialize ( | ||
SigninLogs | ||
// Filter sign-in logs related to the Azure Portal | ||
| where AppDisplayName == "Azure Portal" | ||
// Exclude entries with empty OriginalRequestId | ||
| where isnotempty(OriginalRequestId) | ||
// Summarize various counts and sets based on brute force criteria | ||
| summarize | ||
AzureSuccessfulEvent = countif(ResultType == 0), | ||
AzureFailedEvent = countif(ResultType != 0), | ||
totalAzureLoginEventId = dcount(OriginalRequestId), | ||
AzureFailedEventsCount = dcountif(OriginalRequestId, ResultType != 0), | ||
AzureSuccessfuleventsCount = dcountif(OriginalRequestId, ResultType == 0), | ||
AzureSetOfFailedevents = makeset(iff(ResultType != 0, OriginalRequestId, ""), 5), | ||
AzureSetOfSuccessfulEvents = makeset(iff(ResultType == 0, OriginalRequestId, ""), 5) | ||
by | ||
IPAddress, | ||
UserPrincipalName, | ||
bin(TimeGenerated, 1min), | ||
UserAgent, | ||
ConditionalAccessStatus, | ||
OperationName, | ||
RiskDetail, | ||
AuthenticationRequirement, | ||
ClientAppUsed | ||
// Extracting the name and UPN suffix from UserPrincipalName | ||
| extend | ||
Name = tostring(split(UserPrincipalName, '@')[0]), | ||
UPNSuffix = tostring(split(UserPrincipalName, '@')[1])); | ||
// Materialize a table named "AWS_Bruforce" containing AWS CloudTrail events related to ConsoleLogins within the last 1 day | ||
let AWS_Bruforce = materialize ( | ||
AWSCloudTrail | ||
// Filter CloudTrail events related to ConsoleLogin | ||
| where EventName == "ConsoleLogin" | ||
// Extract ActionType from ResponseElements JSON | ||
| extend ActionType = tostring(parse_json(ResponseElements).ConsoleLogin) | ||
// Summarize various counts and sets based on brute force criteria | ||
| summarize | ||
AWSSuccessful=countif(ActionType == "Success"), | ||
AWSFailed = countif(ActionType == "Failure"), | ||
totalAwsEventId= dcount(AwsEventId), | ||
AWSFailedEventsCount = dcountif(AwsEventId, ActionType == "Failure"), | ||
AWSSuccessfuleventsCount = dcountif(AwsEventId, ActionType == "Success"), | ||
AWSFailedevents = makeset(iff(ActionType == "Failure", AwsEventId, ""), 5), | ||
AWSSuccessfulEvents = makeset(iff(ActionType == "Success", AwsEventId, ""), 5) | ||
// Grouping by various attributes | ||
by | ||
SourceIpAddress, | ||
UserIdentityUserName, | ||
bin(TimeGenerated, 1min), | ||
UserAgent ); | ||
// Joining the Azure_Bruforce and AWS_Bruforce tables on matching IP addresses and UserAgents | ||
Azure_Bruforce | ||
| join kind=inner AWS_Bruforce on $left.IPAddress == $right.SourceIpAddress and $left.UserAgent == $right.UserAgent | ||
// Filtering based on conditions for failed and successful events | ||
| where (AWSFailedEventsCount >= 4 and AzureFailedEventsCount >= 5) and ((AzureSuccessfuleventsCount >= 1 and AzureFailedEvent > AzureSuccessfulEvent) or (AWSSuccessfuleventsCount >= 1 and AWSFailedEventsCount > AWSSuccessfuleventsCount)) | ||
entityMappings: | ||
- entityType: Account | ||
fieldMappings: | ||
- identifier: Name | ||
columnName: Name | ||
- identifier: UPNSuffix | ||
columnName: UPNSuffix | ||
- entityType: IP | ||
fieldMappings: | ||
- identifier: Address | ||
columnName: SourceIpAddress | ||
customDetails: | ||
AwsUser: UserIdentityUserName | ||
UserAgent: UserAgent | ||
AzureUser: UserPrincipalName | ||
AzureClientAppUsed: ClientAppUsed | ||
kind: Scheduled | ||
version: 1.0.0 |
149 changes: 149 additions & 0 deletions
149
Detections/MultipleDataSources/CrossCloudUnauthorizedCredentialsAccessDetection.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
id: 122fbc6a-57ab-4aa7-b9a9-51ac4970cac1 | ||
name: Cross-Cloud Unauthorized Credential Access Detection From AWS RDS Login | ||
description: | | ||
' | ||
This detection correlates AWS GuardDuty Credential Access alerts related to Amazon Relational Database Service (RDS) activity with Azure portal sign-in activities. It identifies successful and failed logins, anomalous behavior, and malicious IP access. By joining these datasets on network entities and IP addresses, it detects unauthorized credential access attempts across AWS and Azure resources, enhancing cross-cloud security monitoring. | ||
' | ||
severity: Medium | ||
requiredDataConnectors: | ||
- connectorId: AzureActiveDirectory | ||
dataTypes: | ||
- SigninLogs | ||
- connectorId: AWSS3 | ||
dataTypes: | ||
- AWSGuardDuty | ||
queryFrequency: 1d | ||
queryPeriod: 1d | ||
triggerOperator: gt | ||
triggerThreshold: 0 | ||
tactics: | ||
- CredentialAccess | ||
- InitialAccess | ||
relevantTechniques: | ||
- T1557 | ||
- T1110 | ||
- T1110.003 | ||
- T1110.004 | ||
- T1606 | ||
- T1556 | ||
- T1133 | ||
query: | | ||
// Define variable 'AwsAlert' to collect AWS GuardDuty CredentialAccess alerts related to Amazon Relational Database Service (RDS) activity | ||
let AwsAlert = materialize ( | ||
AWSGuardDuty | ||
| where ActivityType has_any ( | ||
"CredentialAccess:RDS/TorIPCaller.SuccessfulLogin", | ||
"CredentialAccess:RDS/TorIPCaller.FailedLogin", | ||
"CredentialAccess:RDS/AnomalousBehavior.SuccessfulBruteForce", | ||
"CredentialAccess:RDS/AnomalousBehavior.SuccessfulLogin", | ||
"CredentialAccess:RDS/MaliciousIPCaller.SuccessfulLogin", | ||
"CredentialAccess:RDS/MaliciousIPCaller.FailedLogin" | ||
) | ||
| extend | ||
AWSAlertId = Id, | ||
AWSAlertTitle = Title, | ||
AWSAlertDescription = Description, | ||
AWSresourceType = tostring(parse_json(ResourceDetails).resourceType), | ||
AWSNetworkEntity = tostring(parse_json(ServiceDetails).action.rdsLoginAttemptAction.remoteIpDetails.ipAddressV4), | ||
RDSInstanceId = tostring(parse_json(ResourceDetails).rdsDbInstanceDetails.dbInstanceIdentifier), | ||
RDSUser = tostring(parse_json(ResourceDetails).rdsDbUserDetails.user), | ||
RDSApplication = tostring(parse_json(ResourceDetails).rdsDbUserDetails.application), | ||
RDSactionType = tostring(parse_json(ServiceDetails).action.actionType), | ||
AWSAlertTime = TimeCreated, | ||
AWSAlertLink= tostring(strcat('https://us-east-1.console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?macros=current&fId=',Id)), | ||
Severity = | ||
case ( | ||
Severity >= 7.0, "High", | ||
Severity between (4.0 .. 6.9), "Medium", | ||
Severity between (1.0 .. 3.9), "Low", | ||
"Unknown") | ||
| distinct | ||
AWSAlertTime, | ||
ActivityType, | ||
AWSAlertId, | ||
AWSAlertLink, | ||
AWSAlertTitle, | ||
AWSAlertDescription, | ||
AWSresourceType, | ||
Arn, | ||
Severity, | ||
RDSactionType, | ||
RDSApplication, | ||
RDSInstanceId, | ||
RDSUser, | ||
AWSNetworkEntity | ||
); | ||
// Define variable 'Azure_sigin' to collect Azure portal sign-in activities | ||
let Azure_sigin = materialize ( | ||
SigninLogs | ||
| where AppDisplayName == "Azure Portal" | ||
| where isnotempty(OriginalRequestId) | ||
| summarize | ||
AzureSuccessfulEvent = countif(ResultType == 0), | ||
AzureFailedEvent = countif(ResultType != 0), | ||
totalAzureLoginEventId = dcount(OriginalRequestId), | ||
AzureFailedEventsCount = dcountif(OriginalRequestId, ResultType != 0), | ||
AzureSuccessfuleventsCount = dcountif(OriginalRequestId, ResultType == 0), | ||
AzureSetOfFailedevents = makeset(iff(ResultType != 0, OriginalRequestId, ""), 5), | ||
AzureSetOfSuccessfulEvents = makeset(iff(ResultType == 0, OriginalRequestId, ""), 5) | ||
by | ||
IPAddress, | ||
UserPrincipalName, | ||
bin(TimeGenerated, 1min), | ||
UserAgent, | ||
ConditionalAccessStatus, | ||
OperationName, | ||
RiskDetail, | ||
AuthenticationRequirement, | ||
ClientAppUsed | ||
// Extracting the name and UPN suffix from UserPrincipalName | ||
| extend | ||
Name = tostring(split(UserPrincipalName, '@')[0]), | ||
UPNSuffix = tostring(split(UserPrincipalName, '@')[1]) | ||
); | ||
// Join 'AwsAlert' and 'Azure_sigin' on the AWS Network Entity and Azure IP Address | ||
AwsAlert | ||
| join kind=inner Azure_sigin on $left.AWSNetworkEntity == $right.IPAddress | ||
entityMappings: | ||
- entityType: IP | ||
fieldMappings: | ||
- identifier: Address | ||
columnName: IPAddress | ||
- entityType: Account | ||
fieldMappings: | ||
- identifier: Name | ||
columnName: Name | ||
- identifier: UPNSuffix | ||
columnName: UPNSuffix | ||
customDetails: | ||
AWSAlertUserName: RDSUser | ||
AWSArn: Arn | ||
AWSresourceType: AWSresourceType | ||
AWSInstanceType: RDSactionType | ||
AWSAplicationName: RDSApplication | ||
AWSInstanceId: RDSInstanceId | ||
AzureUserAgent: UserAgent | ||
AzureUser: UserPrincipalName | ||
AzureClientAppUsed: ClientAppUsed | ||
AzConditionalAccess: ConditionalAccessStatus | ||
AzureOperationName: OperationName | ||
AzureRiskDetail: RiskDetail | ||
AzAuthRequirement: AuthenticationRequirement | ||
alertSeverity: Severity | ||
alertDetailsOverride: | ||
alertDisplayNameFormat: "IP address {{IPAddress}} in {{AWSAlertTitle}} seen in Azure Signin Logs with {{UserPrincipalName}}" | ||
alertDescriptionFormat: "This detection correlates AWS GuardDuty Credential Access alert described '{{AWSAlertDescription}}' related to Amazon Relational Database Service (RDS) activity with Azure portal sign-in activities. It identifies successful and failed logins, anomalous behavior, and malicious IP access. By joining these datasets on network entities and IP addresses, it detects unauthorized credential access attempts across AWS and Azure resources, enhancing cross-cloud security monitoring. \n\n AWS ALert Link : '{{AWSAlertLink}}' \n\n Find More Details :https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-active.html" | ||
alertSeverityColumnName: Severity | ||
alertDynamicProperties: | ||
- alertProperty: AlertLink | ||
value: AWSAlertLink | ||
- alertProperty: ProviderName | ||
value: "AWS" | ||
- alertProperty: ProductName | ||
value: "AWSGuardDuty" | ||
- alertProperty: ProductComponentName | ||
value: "AWSGuardDuty" | ||
- alertProperty: Severity | ||
value: Severity | ||
kind: Scheduled | ||
version: 1.0.0 |
123 changes: 123 additions & 0 deletions
123
...pleDataSources/SuccessfulAWSConsoleLoginfromIPAddressObservedConductingPasswordSpray.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
id: 188db479-d50a-4a9c-a041-644bae347d1f | ||
name: Successful AWS Console Login from IP Address Observed Conducting Password Spray | ||
description: | | ||
'This query aims to detect instances of successful AWS console login events followed by multiple failed app logons alerts generated by Microsoft Cloud App Security or password spray alerts generated by Defender Products. | ||
Specifically, it focuses on scenarios where the successful login takes place within a 60-minute timeframe of the high-severity alert. | ||
The login is considered relevant if it originates from an IP address associated with potential attackers.' | ||
severity: Medium | ||
requiredDataConnectors: | ||
- connectorId: AWS | ||
dataTypes: | ||
- AWSCloudTrail | ||
- connectorId: MicrosoftDefenderAdvancedThreatProtection | ||
dataTypes: | ||
- SecurityAlert | ||
- connectorId: AzureActiveDirectoryIdentityProtection | ||
dataTypes: | ||
- SecurityAlert (IPC) | ||
- connectorId: BehaviorAnalytics | ||
dataTypes: | ||
- IdentityInfo | ||
- connectorId: MicrosoftThreatProtection | ||
dataTypes: | ||
- SecurityAlert | ||
queryFrequency: 1d | ||
queryPeriod: 1d | ||
triggerOperator: gt | ||
triggerThreshold: 0 | ||
tactics: | ||
- InitialAccess | ||
- CredentialAccess | ||
relevantTechniques: | ||
- T1110 | ||
- T1078 | ||
query: | | ||
SecurityAlert | ||
// Filtering alerts based on Microsoft product names and Relevent alert names | ||
| where ProductName in ( "Microsoft Cloud App Security","Azure Active Directory Identity Protection") | ||
|where AlertName in ("Multiple failed user log on attempts to an app","Password Spray") | ||
// Parsing and extending the 'Entities' column as JSON objects | ||
| extend Entities = parse_json(Entities) | ||
// Exploring IP entities within the alert entities | ||
| mv-apply Entity = Entities on | ||
( | ||
where Entity.Type == 'ip' | ||
| extend EntityIp = tostring(Entity.Address) | ||
) | ||
// Exploring account entities within the alert entities | ||
| mv-apply Entity = Entities on | ||
( | ||
where Entity.Type == 'account' | ||
| extend AccountObjectId = tostring(Entity.AadUserId) | ||
) | ||
// Filtering out alerts with missing IP or account information | ||
| where isnotempty(EntityIp) and isnotempty(AccountObjectId) | ||
// Summarizing relevant fields for further analysis | ||
| summarize | ||
by | ||
AlertName, | ||
ProductName, | ||
ProviderName, | ||
AlertSeverity, | ||
EntityIp, | ||
Tactics, | ||
Techniques, | ||
AlertTime= bin(TimeGenerated, 1min), | ||
AccountObjectId, | ||
AlertTimeGenerated=TimeGenerated | ||
// Joining with IdentityInfo to obtain additional account details | ||
| join kind=inner ( | ||
IdentityInfo | ||
| where TimeGenerated >= ago(1d) | ||
| distinct AccountObjectId, AccountUPN=tolower(AccountUPN) | ||
) | ||
on AccountObjectId | ||
|extend Name = tostring(split(AccountUPN,'@')[0]), UPNSuffix =tostring(split(AccountUPN,'@')[1]) | ||
// Joining with AWSCloudTrail data to correlate AWS console logins | ||
| join kind=inner ( | ||
AWSCloudTrail | ||
| where EventName == "ConsoleLogin" | ||
| extend CTUPN= tolower(tostring(tolower(split(UserIdentityArn, "/", 2)[0]))) | ||
| extend ActionType= tostring(parse_json(ResponseElements).ConsoleLogin) | ||
| where ActionType == "Success" | ||
| extend AWSTime= bin(TimeGenerated, 1min) | ||
| project | ||
EventName, | ||
EventSource, | ||
EventTypeName, | ||
RecipientAccountId, | ||
ResponseElements, | ||
SessionMfaAuthenticated, | ||
SourceIpAddress, | ||
TimeGenerated, | ||
UserAgent, | ||
UserIdentityArn, | ||
UserIdentityType, | ||
CTUPN, | ||
AWSTime, | ||
UserIdentityUserName | ||
) | ||
on $left.EntityIp == $right.SourceIpAddress | ||
// Filtering login event after the Alert generation time | ||
| where AlertTimeGenerated between ((AWSTime - 1h)..(AWSTime + 1h)) | ||
// Calculating the time difference between alert generation and AWS login | ||
| extend timediff = datetime_diff('minute', AlertTimeGenerated, TimeGenerated) | ||
// Filtering alerts with a time difference of up to 60 minutes | ||
| where timediff <= 60 | ||
entityMappings: | ||
- entityType: Account | ||
fieldMappings: | ||
- identifier: Name | ||
columnName: Name | ||
- identifier: UPNSuffix | ||
columnName: UPNSuffix | ||
- entityType: IP | ||
fieldMappings: | ||
- identifier: Address | ||
columnName: SourceIpAddress | ||
customDetails: | ||
AWSUser: UserIdentityArn | ||
UserAgent: UserAgent | ||
AWSUserUPN: CTUPN | ||
kind: Scheduled | ||
version: 1.0.0 |
Oops, something went wrong.