Query Details

Purple Knight Reconnaissance Detected

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

Explanation

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.

Details

Fabian Bader profile picture

Fabian Bader

Released: October 14, 2023

Tables

MicrosoftGraphActivityLogs

Keywords

DevicesIntuneUserMicrosoft Graphreconnaissanceattackservice principalT1595T1589T1591T1078GraphQueriesMicrosoftGraphActivityLogsObjectIdObjectTypeNormalizedRequestUriGraphEndpointsCalledIPAddressesConfidenceScoreIPEntitiesIPInformationsuppressionEnabledincidentConfigurationcreateIncidentgroupingConfigurationenabledreopenClosedIncidentlookbackDurationmatchingMethodgroupByEntitiesgroupByAlertDetailsgroupByCustomDetailseventGroupingSettingsaggregationKindAlertPerResultalertDetailsOverridealertDynamicPropertiesentityTypeAccountfieldMappingsidentifierAadUserIdcolumnNamesentinelEntitiesMappingssuppressionDuration.

Operators

|=:.extendwhereiffreplace_regexmake_setsummarizeprojectroundtodoublearray_lengthset_intersectmv-expandsortrow_numberbag_packjoinproject-away

Severity

Medium

Tactics

ReconnaissanceInitialAccess

MITRE Techniques

Frequency: 30m

Period: 40m

Actions

GitHub