Query Details
id: 0833c2f0-036c-479b-90c4-59bd98f8c698
name: Purple Knight reconnaissance detected
version: 1.0.0
kind: Scheduled
description: Microsoft Graph queries that are similar to Purple Knight where detected in your environment. An attacker might have started with the reconnaissance stage of an attack in your environment. This requires access to a compromised service principal.
severity: Medium
queryFrequency: 30m
queryPeriod: 40m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Reconnaissance
- InitialAccess
relevantTechniques:
- T1595
- T1589
- T1591
- T1078
query: |-
let GraphQueries = dynamic([
"https:/graph.microsoft.com/version/servicePrincipals/<UUID>/appRoleAssignments",
"https:/graph.microsoft.com/version/roleManagement/directory/roleEligibilityScheduleInstances",
"https:/graph.microsoft.com/version/servicePrincipals/",
"https:/graph.microsoft.com/version/roleManagement/directory/roleAssignments",
"https:/graph.microsoft.com/version/users/<UUID>/memberOf",
"https:/graph.microsoft.com/version/directoryRoles/roleTemplateId=<UUID>/members",
"https:/graph.microsoft.com/version/directoryObjects/<UUID>",
"https:/graph.microsoft.com/version/identity/conditionalAccess/policies",
"https:/graph.microsoft.com/version/policies/authorizationPolicy",
"https:/graph.microsoft.com/version/policies/identitySecurityDefaultsEnforcementPolicy",
"https:/graph.microsoft.com/version/organization",
"https:/graph.microsoft.com/version/users",
"https:/graph.microsoft.com/version/reports/credentialUserRegistrationDetails",
"https:/graph.microsoft.com/version/directoryRoles",
"https:/graph.microsoft.com/version/identity/conditionalAccess/namedLocations",
"https:/graph.microsoft.com/version/auditLogs/signIns",
"https:/graph.microsoft.com/version/$batch",
"https:/graph.microsoft.com/version/roleManagement/directory/roleAssignmentScheduleRequests",
"https:/graph.microsoft.com/version/directory/administrativeUnits",
"https:/graph.microsoft.com/version/settings",
"https:/graph.microsoft.com/version/applications",
"https:/graph.microsoft.com/version/authenticationMethodsPolicy/authenticationMethodConfigurations/MicrosoftAuthenticator",
"https:/graph.microsoft.com/version/servicePrincipals"
]);
let PotentialMaliciousGraphCalls = materialize (
MicrosoftGraphActivityLogs
| where ingestion_time() > ago(35m)
| extend ObjectId = iff(isempty(UserId), ServicePrincipalId, UserId)
| extend ObjectType = iff(isempty(UserId), "ServicePrincipalId", "UserId")
| where RequestUri !has "microsoft.graph.delta"
| extend NormalizedRequestUri = replace_regex(RequestUri, @'[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}', @'<UUID>')
| extend NormalizedRequestUri = replace_regex(NormalizedRequestUri, @'\d+$', @'<UUID>')
| extend NormalizedRequestUri = replace_regex(NormalizedRequestUri, @'\/+', @'/')
| extend NormalizedRequestUri = replace_regex(NormalizedRequestUri, @'\/(v1\.0|beta)\/', @'/version/')
| extend NormalizedRequestUri = replace_regex(NormalizedRequestUri, @'%23EXT%23', @'')
| extend NormalizedRequestUri = replace_regex(NormalizedRequestUri, @'\/[a-zA-Z0-9+_.\-]+@[a-zA-Z0-9.]+\/', @'/<UUID>/')
| extend NormalizedRequestUri = replace_regex(NormalizedRequestUri, @'^\/<UUID>', @'')
| extend NormalizedRequestUri = replace_regex(NormalizedRequestUri, @'\?.*$', @'')
| summarize
GraphEndpointsCalled = make_set(NormalizedRequestUri, 1000),
IPAddresses = make_set(IpAddress)
by ObjectId, ObjectType
| project
ObjectId,
ObjectType,
IPAddresses,
MatchingQueries=set_intersect(GraphQueries, GraphEndpointsCalled)
| extend ConfidenceScore = round(todouble(array_length(MatchingQueries)) / todouble(array_length(GraphQueries)), 1)
| where ConfidenceScore > 0.7);
let IPEntities = PotentialMaliciousGraphCalls
| mv-expand IPAddresses
| sort by ObjectId
| extend CurrentRowNumber=row_number(2, prev(ObjectId) != ObjectId)
| extend IPInformation = bag_pack(@"$id", CurrentRowNumber, "Address", IPAddresses, "Type", "ip")
| project ObjectId, IPInformation
| summarize IPInformation = make_set(IPInformation, 150) by ObjectId;
PotentialMaliciousGraphCalls
| join kind=leftouter IPEntities on ObjectId
| project-away IPAddresses, *1, *2
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: 5h
matchingMethod: AllEntities
groupByEntities: []
groupByAlertDetails: []
groupByCustomDetails: []
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDynamicProperties:
- alertProperty: ConfidenceScore
value: ConfidenceScore
entityMappings:
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: ObjectId
sentinelEntitiesMappings:
- columnName: IPInformation
suppressionDuration: 5h
The query is designed to detect potential malicious activity in your environment related to Microsoft Graph queries. It looks for specific endpoints that may indicate reconnaissance or initial access by an attacker. The query checks for any calls to these endpoints within a certain time frame and calculates a confidence score based on the number of matching queries. It also identifies the IP addresses associated with these calls. The query can be customized to suppress alerts and configure incident creation. The results can be grouped based on entities and alert details. The query also includes entity and IP address mappings for further analysis.

Fabian Bader
Released: October 14, 2023
Tables
Keywords
Operators