← Back to SOC feed Coverage →

Successful Sign-In From Non-Compliant Device with bulk download activity

kql MEDIUM Azure-Sentinel
T1078T1087
AuditLogsSigninLogs
backdoorhuntingmicrosoftofficial
This rule was pulled from an open-source repository and enriched with AI. Validate in a test environment before deploying to production.
View original rule at Azure-Sentinel →
Retrieved: 2026-06-03T11:00:00Z · Confidence: medium

Hunt Hypothesis

Attackers may use non-compliant devices to perform bulk downloads, potentially exfiltrating sensitive data or enumerating internal resources. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential lateral movement or data exfiltration attempts.

KQL Query

SigninLogs
  | where ResultType == 0
  | where tostring(DeviceDetail.isCompliant) == "false"
  | where ConditionalAccessStatus == "success"
  | extend UserPrincipalName = tolower(UserPrincipalName)
  | project TimeGenerated,ResourceId, OperationName,CorrelationId,Location,AppId, Id,IPAddress,LocationDetails,OriginalRequestId,ResourceServicePrincipalId,UserId,UserPrincipalName,AADTenantId,UserType,HomeTenantId,ConditionalAccessStatus
| join kind=inner
(
AuditLogs
| where OperationName has_any  ("Download group members", "Download users", "Download user registeration details", "Download groups")
| extend IpAddress = case(
isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)) and tostring(parse_json(tostring(InitiatedBy.user)).ipAddress) != 'null', tostring(parse_json(tostring(InitiatedBy.user)).ipAddress),
isnotempty(tostring(parse_json(tostring(InitiatedBy.app)).ipAddress)) and tostring(parse_json(tostring(InitiatedBy.app)).ipAddress) != 'null', tostring(parse_json(tostring(InitiatedBy.app)).ipAddress),'Not Available')
| extend InitiatedBy = iff(isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)),
  tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName), tostring(parse_json(tostring(InitiatedBy.app)).displayName)), UserRoles = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)
| extend TargetResourceName = tolower(tostring(TargetResources.[0].displayName))
| extend InitiatedBy = tolower(InitiatedBy)
| project TimeGenerated, InitiatedBy, CorrelationId, Id,AADTenantId, ResourceId, IpAddress, OperationName
) on $left.UserPrincipalName == $right.InitiatedBy
| project TimeGenerated, UserPrincipalName, IPAddress, CorrelationId, Id, ResourceId, OperationName

Analytic Rule Definition

id: a5bb38e3-5ee2-47fe-a65d-c3c9341112ef
name: Successful Sign-In From Non-Compliant Device with bulk download activity
description: |
  'This hunting query will help detect successful sign-ins from devices that are marked non-compliant along with bulk download activity.
  Attackers may attempt to get a list of accounts, groups, registration details within an environment which could help in follow-on behavior.
  Best practice is to block sign ins from non-complaint devices, however if allowed monitor these events to ensure they do not lead to other risky activity.
    Reference: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-devices#non-compliant-device-sign-in'
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
      - AuditLogs
tactics:
  - InitialAccess
  - Discovery
relevantTechniques:
  - T1078
  - T1087
query: |
  SigninLogs
    | where ResultType == 0
    | where tostring(DeviceDetail.isCompliant) == "false"
    | where ConditionalAccessStatus == "success"
    | extend UserPrincipalName = tolower(UserPrincipalName)
    | project TimeGenerated,ResourceId, OperationName,CorrelationId,Location,AppId, Id,IPAddress,LocationDetails,OriginalRequestId,ResourceServicePrincipalId,UserId,UserPrincipalName,AADTenantId,UserType,HomeTenantId,ConditionalAccessStatus
  | join kind=inner
  (
  AuditLogs
  | where OperationName has_any  ("Download group members", "Download users", "Download user registeration details", "Download groups")
  | extend IpAddress = case(
  isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)) and tostring(parse_json(tostring(InitiatedBy.user)).ipAddress) != 'null', tostring(parse_json(tostring(InitiatedBy.user)).ipAddress),
  isnotempty(tostring(parse_json(tostring(InitiatedBy.app)).ipAddress)) and tostring(parse_json(tostring(InitiatedBy.app)).ipAddress) != 'null', tostring(parse_json(tostring(InitiatedBy.app)).ipAddress),'Not Available')
  | extend InitiatedBy = iff(isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)),
    tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName), tostring(parse_json(tostring(InitiatedBy.app)).displayName)), UserRoles = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)
  | extend TargetResourceName = tolower(tostring(TargetResources.[0].displayName))
  | extend InitiatedBy = tolower(InitiatedBy)
  | project TimeGenerated, InitiatedBy, CorrelationId, Id,AADTenantId, ResourceId, IpAddress, OperationName
  ) on $left.UserPrincipalName == $right.InitiatedBy
  | project TimeGenerated, UserPrincipalName, IPAddress, CorrelationId, Id, ResourceId, OperationName
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress

Required Data Sources

Sentinel TableNotes
AuditLogsEnsure this data connector is enabled
SigninLogsEnsure this data connector is enabled

MITRE ATT&CK Context

References

False Positive Guidance

Original source: https://github.com/Azure/Azure-Sentinel/blob/main/Hunting Queries/MultipleDataSources/NonCompliantSigninwithBulkDownload.yaml