Detection rules › Kusto

Kusto rules: 3g

1Password - Manual account creation

#
Severity
medium
Time window
5m
Source
github.com/Azure/Azure-Sentinel

This will alert when a new account was created manually within 1Password. This should only be used when a 1Password integration via a SCIM Bridge has been implemented. Ref: https://support.1password.com/scim/ Ref: https://1password.com/ Ref: https://github.com/securehats/

MITRE ATT&CK Coverage

TacticTechniques
Persistence

Telemetry Coverage

PlatformRecord / event type
1Passwordinvite-create: Create Invite

Rule Body

id: 9a264487-bcb8-4c7f-a461-b289a46377b8
name: 1Password - Manual account creation
version: 1.0.0
kind: Scheduled
description: |-
  This will alert when a new account was created manually within 1Password. This should only be used when a 1Password integration via a SCIM Bridge has been implemented.

  Ref: https://support.1password.com/scim/
  Ref: https://1password.com/
  Ref: https://github.com/securehats/
severity: Medium
requiredDataConnectors:
  - connectorId: 1Password
    dataTypes:
      - OnePasswordEventLogs_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1136
query: |-
  OnePasswordEventLogs_CL
  | where action == "create"
  | where object_type == "invite"
  | where actor_details.email !endswith "@1passwordserviceaccounts.com"
  | extend
      ActorUsername = actor_details.email
      , SrcIpAddr = session.ip
      , TargetUsername = aux_info
suppressionEnabled: false
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AnyAlert
suppressionDuration: 1h
eventGroupingSettings:
  aggregationKind: SingleAlert
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: ActorUsername
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: SrcIpAddr
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: TargetUsername

Stages and Predicates

Stage 1: source

OnePasswordEventLogs_CL

Stage 2: where

| where action == "create"

Stage 3: where

| where object_type == "invite"

Stage 4: where

| where actor_details.email !endswith "@1passwordserviceaccounts.com"

Stage 5: extend

| extend
    ActorUsername = actor_details.email
    , SrcIpAddr = session.ip
    , TargetUsername = aux_info

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
actor_details.emailends_with@1passwordserviceaccounts.comexcludes:actor_details.email field:"actor_details.email" value:"@1passwordserviceaccounts.com"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActorUsernameextend
SrcIpAddrextend
TargetUsernameextend

Apache - Apache 2.4.49 flaw CVE-2021-41773

#
Status
available
Severity
high
Time window
10m
Source
github.com/Azure/Azure-Sentinel

'Detects using Apache 2.4.49 flaw CVE-2021-41773'

MITRE ATT&CK Coverage

Rule Body

id: 767f9dc4-3b01-11ec-8d3d-0242ac130003
name: Apache - Apache 2.4.49 flaw CVE-2021-41773 
description: |
  'Detects using Apache 2.4.49 flaw CVE-2021-41773'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: CustomLogsAma
    datatypes:
      - ApacheHTTPServer_CL
queryFrequency: 10m
queryPeriod: 10m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - LateralMovement
relevantTechniques:
  - T1190
  - T1133
  - T1210
query: |
  ApacheHTTPServer
  | where HttpRequestMethod =~ "GET"
  | where UrlOriginal has_all ("/cgi-bin/", "/%2e%2e/")
  | where HttpStatusCode == "200" 
  | extend UrlCustomEntity = UrlOriginal
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: UrlCustomEntity
version: 1.0.3
kind: Scheduled

Stages and Predicates

Stage 1: source

ApacheHTTPServer

Stage 2: where

| where HttpRequestMethod =~ "GET"

Stage 3: where

| where UrlOriginal has_all ("/cgi-bin/", "/%2e%2e/")

Stage 4: where

| where HttpStatusCode == "200"

Stage 5: extend

| extend UrlCustomEntity = UrlOriginal

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
UrlCustomEntityextend

Archive Repository Deleted

#
Status
available
Severity
high
Time window
5m
Source
github.com/Azure/Azure-Sentinel

Detects when an archive repository is deleted from Veeam Backup & Replication. This might indicate unauthorized removal of critical components.

Rule Body

id: 6a90f177-dcaa-44ec-b6e6-723ee8408cb2
name: Archive Repository Deleted
description: Detects when an archive repository is deleted from Veeam Backup & Replication.
  This might indicate unauthorized removal of critical components.
severity: High
status: Available
requiredDataConnectors:
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: SyslogAma
  dataTypes:
  - Syslog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 29900\n| extend Name = extract(\"\
  Name=\\\"([^\\\"]*)\\\"\", 1, SyslogMessage)\n| project\n    Date = format_datetime(TimeGenerated,\
  \ 'dd.MM.yyyy HH:mm'),\n    DataSource = original_host,\n    EventId = instanceId,\n\
  \    [\"User Name\"] = user,\n    [\"Object Name\"] = Name,\n    MessageDetails\
  \ = Description,\n   Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
  Date: Date
  VbrHostName: DataSource
  EventId: EventId
  MessageDetails: MessageDetails
  Severity: Severity

Stages and Predicates

Stage 1: source

Veeam_GetSecurityEvents

Stage 2: where

| where instanceId == 29900

Stage 3: extend

| extend Name = extract("Name=\"([^\"]*)\"", 1, SyslogMessage)

Stage 4: project

| project
    Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
    DataSource = original_host,
    EventId = instanceId,
    ["User Name"] = user,
    ["Object Name"] = Name,
    MessageDetails = Description,
   Severity = SeverityDescription

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DataSourceproject
Dateproject
EventIdproject
MessageDetailsproject
Object Nameproject
Severityproject
User Nameproject

CTE - Device Repeatedly Blocked Across Multiple Days

#
Severity
medium
Time window
7d
Group by
DeviceId, DeviceOs, DeviceUser, HostName, TenantId
Source
github.com/Azure/Azure-Sentinel

A device has triggered URL blocks on 3 or more distinct days in the past week, suggesting a recurring risky browsing pattern or persistent exposure.

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 8fbb095d-3a35-483b-b264-25b858a52f4d
name: CTE - Device Repeatedly Blocked Across Multiple Days
description: |
  A device has triggered URL blocks on 3 or more distinct days in the past week, suggesting a recurring risky browsing pattern or persistent exposure.
severity: Medium
requiredDataConnectors:
  - connectorId: cybereinforce_cte
    dataTypes:
      - CybereinforceCTE_CL
queryFrequency: 12h
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1189
query: |
  CybereinforceCTE_CL
  | where EventType == "block.url"
  | extend d = parse_json(Details)
  | extend
      DeviceId = tostring(d.deviceId),
      HostName = iff(isnotempty(tostring(d.hostname)), tostring(d.hostname), strcat("cte-device-", substring(EntityId, 0, 8))),
      DeviceUser = tostring(d.deviceUser),
      DeviceOs = tostring(d.deviceOs),
      DeviceVersion = tostring(d.deviceVersion),
      RuleId = tostring(d.ruleId),
      RuleName = iff(isnotempty(tostring(d.ruleName)), tostring(d.ruleName), "Pattern Match"),
      Pattern = tostring(d.pattern),
      Url = tostring(d.url),
      Action = tostring(d.action),
      IsIoc = tobool(d.ioc),
      ClientIp = tostring(split(tostring(d.clientIp), ":")[0]),
      ClientPort = tostring(split(tostring(d.clientIp), ":")[1]),
      UserAgent = tostring(d.userAgent)
  | summarize BlockDays=dcount(startofday(TimeGenerated)), BlockCount=count(), SampleUrls=make_set(Url, 20) by DeviceId, HostName, DeviceUser, DeviceOs, TenantId
  | where BlockDays >= 3
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

CybereinforceCTE_CL

Stage 2: where

| where EventType == "block.url"

Stage 3: extend

| extend d = parse_json(Details)

Stage 4: extend

| extend
    DeviceId = tostring(d.deviceId),
    HostName = iff(isnotempty(tostring(d.hostname)), tostring(d.hostname), strcat("cte-device-", substring(EntityId, 0, 8))),
    DeviceUser = tostring(d.deviceUser),
    DeviceOs = tostring(d.deviceOs),
    DeviceVersion = tostring(d.deviceVersion),
    RuleId = tostring(d.ruleId),
    RuleName = iff(isnotempty(tostring(d.ruleName)), tostring(d.ruleName), "Pattern Match"),
    Pattern = tostring(d.pattern),
    Url = tostring(d.url),
    Action = tostring(d.action),
    IsIoc = tobool(d.ioc),
    ClientIp = tostring(split(tostring(d.clientIp), ":")[0]),
    ClientPort = tostring(split(tostring(d.clientIp), ":")[1]),
    UserAgent = tostring(d.userAgent)
HostName =
ifisnotempty(tostring(d.hostname))tostring(d.hostname)
elsestrcat("cte-device-", substring(EntityId, 0, 8))
RuleName =
ifisnotempty(tostring(d.ruleName))tostring(d.ruleName)
else"Pattern Match"

Stage 5: summarize

| summarize BlockDays=dcount(startofday(TimeGenerated)), BlockCount=count(), SampleUrls=make_set(Url, 20) by DeviceId, HostName, DeviceUser, DeviceOs, TenantId

Stage 6: where

| where BlockDays >= 3

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
BlockCountsummarize
BlockDayssummarize
DeviceIdsummarize
DeviceOssummarize
DeviceUsersummarize
HostNamesummarize
SampleUrlssummarize
TenantIdsummarize

Cyble Vision Alerts Flash Report

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Status
available
Severity
low
Time window
30m
Source
github.com/Azure/Azure-Sentinel

'Detects new threat intelligence flash reports from CybleVision. Extracts company-level context and report identifiers for triage.'

MITRE ATT&CK Coverage

Rule Body

id: 2c86652a-bbbe-4a32-8b1c-4b53aad0750e
name: Cyble Vision Alerts Flash Report
description: |
  'Detects new threat intelligence flash reports from CybleVision. Extracts company-level context and report identifiers for triage.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: CybleVisionAlerts
  dataTypes:
  - CybleVisionAlerts_CL
enabled: true
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: PT5H
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics:
- Reconnaissance
relevantTechniques:
- T1598
- T1589
subTechniques: []
query: |
  Alerts_flash_report 
  | where Service == "flash_report" 
  | extend MappedSeverity = Severity
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
alertDetailsOverride:
  alertDisplayNameFormat: CybleVision Flash Report {{KeywordName}}
  alertDescriptionFormat: |
    A flash threat report has been published. Report ID {{FR_ReportID}} For Company {{FR_ForCompany}} Description {{Description}}
  alertDynamicProperties: []
customDetails:
  MappedSeverity: Severity
  Status: Status
  AlertID: AlertID
  Service: Service
  FlashReportID: FR_ReportID
  ForCompany: FR_ForCompany
  Tags: FR_Tags
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: ObjectGuid
    columnName: FR_ReportID
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

Alerts_flash_report

Stage 2: where

| where Service == "flash_report"

Stage 3: extend

| extend MappedSeverity = Severity

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Serviceeq
  • flash_report
field:"Service" kind:eq value:"flash_report"

Output Fields

These fields are emitted when the rule matches.

FieldSource
MappedSeverityextend

Cyble Vision Alerts IOC'S

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Status
available
Severity
low
Time window
30m
Source
github.com/Azure/Azure-Sentinel

'Detects malicious Indicators of Compromise such as IPs, domains, URLs, and hashes. Extracts IOC type, behaviour tags, risk rating, and timestamps using Alerts_IOCs parser. Triggers an incident with mapped entities, severity, and details.'

MITRE ATT&CK Coverage

Rule Body

id: c8cf42d5-8684-435f-9c4d-9dd0cc47eaec
name: Cyble Vision Alerts IOC'S
description: |
  'Detects malicious Indicators of Compromise such as IPs, domains, URLs, and hashes. Extracts IOC type, behaviour tags, risk rating, and timestamps using Alerts_IOCs parser. Triggers an incident with mapped entities, severity, and details.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: CybleVisionAlerts
  dataTypes:
  - CybleVisionAlerts_CL
enabled: true
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: PT5H
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics:
- Reconnaissance
- InitialAccess
- Discovery
- CommandAndControl
- Impact
relevantTechniques:
- T1595
- T1133
- T1046
- T1071
- T1486
subTechniques: []
query: |
  Alerts_iocs 
  | where Service == "iocs" 
  | extend MappedSeverity = Severity
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
alertDetailsOverride:
  alertDisplayNameFormat: CybleVision IOC Detected {{IOC_Value}}
  alertDescriptionFormat: |
    Type {{IOC_Type}} Behaviour {{IOC_BehaviourTags}} Risk Rating {{IOC_RiskRating}}'
  alertDynamicProperties: []
customDetails:
  MappedSeverity: Severity
  Status: Status
  AlertID: AlertID
  Service: Service
  IOC: IOC_Value
  IOCType: IOC_Type
  IOCBehaviour: IOC_BehaviourTags
  IOCRiskRating: IOC_RiskRating
  IOCConfidentRating: IOC_ConfidentRating
  IOCAttackName: IOC_AttackName
  FirstSeen: IOC_FirstSeen
  LastSeen: IOC_LastSeen
  Reference: IOC_ReferenceLink
  UUID: IOC_UUID
  Tags: IOC_Tags
entityMappings:
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: IOC_Value
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

Alerts_iocs

Stage 2: where

| where Service == "iocs"

Stage 3: extend

| extend MappedSeverity = Severity

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
MappedSeverityextend

CYFIRMA - High severity File Hash Indicators with Monitor Action Rule

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Severity
high
Time window
5m
Source
github.com/Azure/Azure-Sentinel

"This query retrieves file hash indicators marked for Monitoring, with no assigned role, from the CyfirmaIndicators_CL table. It extracts MD5, SHA1, and SHA256 hashes and includes threat metadata for use in preventive security controls such as EDRs, threat intel platforms."

MITRE ATT&CK Coverage

Rule Body

id: 952b7d77-a848-4888-a638-62fe877eb55d
name: CYFIRMA - High severity File Hash Indicators with Monitor Action Rule
description: |
  "This query retrieves file hash indicators marked for Monitoring, with no assigned role, from the CyfirmaIndicators_CL table. 
  It extracts MD5, SHA1, and SHA256 hashes and includes threat metadata for use in preventive security controls such as EDRs, threat intel platforms."
version: 1.0.1
kind: Scheduled
severity: High
enabled: false
requiredDataConnectors:
  - connectorId: CyfirmaCyberIntelligenceDC
    dataTypes:
      - CyfirmaIndicators_CL
query: |
  // File Hash Indicators with Monitor Action
  let timeFrame = 5m;
  CyfirmaIndicators_CL 
  | where ConfidenceScore >= 80
      and TimeGenerated between (ago(timeFrame) .. now())
      and pattern contains 'file:hashes' and RecommendedActions has 'Monitor' and (isempty(Roles) or not(Roles has_any ('Malware', 'Trojan')))
  | extend MD5 = extract(@"file:hashes\.md5\s*=\s*'([a-fA-F0-9]{32})'", 1, pattern)
  | extend SHA1 = extract(@"file:hashes\.'SHA-1'\s*=\s*'([a-fA-F0-9]{40})'", 1, pattern)
  | extend SHA256 = extract(@"file:hashes\.'SHA-256'\s*=\s*'([a-fA-F0-9]{64})'", 1, pattern)
  | extend
      Algo_MD5='MD5',
      Algo_SHA1= 'SHA1',
      Algo_SHA256='SHA256',
      ProviderName = 'CYFIRMA',
      ProductName = 'DeCYFIR/DeTCT'
  | project  
      MD5,
      SHA1,
      SHA256,
      Algo_MD5,
      Algo_SHA1,
      Algo_SHA256,
      ThreatActors,
      Sources,
      RecommendedActions,
      Roles,
      Country,
      name,
      Description,
      ConfidenceScore,
      SecurityVendors,
      IndicatorID,
      created,
      modified,
      valid_from,
      Tags,
      ThreatType,
      TimeGenerated,
      ProductName,
      ProviderName
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: 5m
suppressionEnabled: true
tactics:
  - Execution
  - InitialAccess
  - DefenseEvasion
  - Impact
relevantTechniques:
  - T1204
  - T1566
  - T1027
  - T1486
  - T1566.001
alertDetailsOverride:
  alertDisplayNameFormat: "High-Confidence File Hash Indicators with Monitor Action - {{name}} "
  alertDescriptionFormat: "{{Description}} - {{name}} "
  alertDynamicProperties:
    - alertProperty: ProductName
      value: ProductName
    - alertProperty: ProviderName
      value: ProviderName
customDetails:
  ThreatActors: ThreatActors
  Sources: Sources
  RecommendedActions: RecommendedActions
  Roles: Roles
  Country: Country
  Description: Description
  ConfidenceScore: ConfidenceScore
  SecurityVendors: SecurityVendors
  IndicatorID: IndicatorID
  Created: created
  Modified: modified
  ValidFrom: valid_from
  Tags: Tags
  ThreatType: ThreatType
  TimeGenerated: TimeGenerated
entityMappings:
  - entityType: FileHash
    fieldMappings:
      - identifier: Algorithm
        columnName: Algo_MD5
      - identifier: Value
        columnName: MD5
  - entityType: FileHash
    fieldMappings:
      - identifier: Algorithm
        columnName: Algo_SHA1
      - identifier: Value
        columnName: SHA1
  - entityType: FileHash
    fieldMappings:
      - identifier: Algorithm
        columnName: Algo_SHA256
      - identifier: Value
        columnName: SHA256
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: AlertPerResult

Stages and Predicates

Parameters

let timeFrame = 5m;

Stage 1: source

CyfirmaIndicators_CL

Stage 2: where

| where ConfidenceScore >= 80
    and TimeGenerated between (ago(timeFrame) .. now())
    and pattern contains 'file:hashes' and RecommendedActions has 'Monitor' and (isempty(Roles) or not(Roles has_any ('Malware', 'Trojan')))

Stage 3: extend (4 consecutive steps)

| extend MD5 = extract(@"file:hashes\.md5\s*=\s*'([a-fA-F0-9]{32})'", 1, pattern)
| extend SHA1 = extract(@"file:hashes\.'SHA-1'\s*=\s*'([a-fA-F0-9]{40})'", 1, pattern)
| extend SHA256 = extract(@"file:hashes\.'SHA-256'\s*=\s*'([a-fA-F0-9]{64})'", 1, pattern)
| extend
    Algo_MD5='MD5',
    Algo_SHA1= 'SHA1',
    Algo_SHA256='SHA256',
    ProviderName = 'CYFIRMA',
    ProductName = 'DeCYFIR/DeTCT'

Stage 4: project

| project  
    MD5,
    SHA1,
    SHA256,
    Algo_MD5,
    Algo_SHA1,
    Algo_SHA256,
    ThreatActors,
    Sources,
    RecommendedActions,
    Roles,
    Country,
    name,
    Description,
    ConfidenceScore,
    SecurityVendors,
    IndicatorID,
    created,
    modified,
    valid_from,
    Tags,
    ThreatType,
    TimeGenerated,
    ProductName,
    ProviderName

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Algo_MD5project
Algo_SHA1project
Algo_SHA256project
ConfidenceScoreproject
Countryproject
Descriptionproject
IndicatorIDproject
MD5project
ProductNameproject
ProviderNameproject
RecommendedActionsproject
Rolesproject
SHA1project
SHA256project
SecurityVendorsproject
Sourcesproject
Tagsproject
ThreatActorsproject
ThreatTypeproject
TimeGeneratedproject
createdproject
modifiedproject
nameproject
valid_fromproject

Dataminr - urgent alerts detected

#
Status
available
Severity
medium
Time window
5m
Source
github.com/Azure/Azure-Sentinel

'Rule helps to detect whenever there is an alert found which has urgent alert-type in Dataminr.'

MITRE ATT&CK Coverage

TacticTechniques
Persistence

Rule Body

id: "64a46029-3236-4d03-b5df-207366a623f1"
name: "Dataminr - urgent alerts detected"
description: |
  'Rule helps to detect whenever there is an alert found which has urgent alert-type in Dataminr.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: DataminrPulseAlerts
    dataTypes:
      - DataminrPulseAlerts
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: GreaterThan
triggerThreshold: 0
tactics: 
  - Persistence
relevantTechniques:
  - T1546
query: |
    DataminrPulseAlerts
    | where EventSource in~ ('Greynoise', 'Shodan', 'VirusTotal', 'alienvault open threat exchange', 'urlscan')
      and AlertType == "Urgent"
incidentConfiguration:
    createIncident: true
eventGroupingSettings:
    aggregationKind: SingleAlert
alertDetailsOverride:
    alertDisplayNameFormat: 'Dataminr Alert: {{AlertId}}'
    alertDescriptionFormat: '{{Caption}}'
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: PostLink
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

DataminrPulseAlerts

Stage 2: where

| where EventSource in~ ('Greynoise', 'Shodan', 'VirusTotal', 'alienvault open threat exchange', 'urlscan')
  and AlertType == "Urgent"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
AlertTypeeq
  • Urgent
field:"AlertType" kind:eq value:"Urgent"
EventSourcein
  • Greynoise
  • Shodan
  • VirusTotal
  • alienvault open threat exchange
  • urlscan
field:"aws::eventSource" kind:in

Dataverse - Suspicious security role modifications

#
Status
available
Severity
medium
Time window
14d
Group by
$left.RoleId, $right.DeletedRoleID, $right.MemberAddedRoleId, $right.MemberRemovedRoleId, InstanceUrl, UserId
Source
github.com/Azure/Azure-Sentinel

Identifies an unusual pattern of events whereby a new role is created followed by the creator adding members to the role and subsequently removing the member or deleting the role after a short time period.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: e44a58b2-b63a-4eb9-92da-85660d73495c
kind: Scheduled
name: Dataverse - Suspicious security role modifications
description: Identifies an unusual pattern of events whereby a new role is created
  followed by the creator adding members to the role and subsequently removing the
  member or deleting the role after a short time period.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Dataverse
    dataTypes:
      - DataverseActivity
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - PrivilegeEscalation
relevantTechniques:
  - T1404
  - T1626
  - T1548
query: |
  let role_create_watch_period = 2d;
  let query_frequency = 1h;
  let role_create_add_events= DataverseActivity
      | where Message == "Create" and EntityName == "role"
      | mv-expand Role = Fields
      | extend RoleName = Role.Value
      | where Role.Name == "name"
      | mv-expand Role = Fields
      | extend RoleCreateTime = TimeGenerated, RoleId = tostring(Role.Value)
      | where Role.Name == "roleid"
      | join kind=inner (
          DataverseActivity
          | where Message == "Associate" and EntityName == "systemuser"
          | mv-expand Role = Fields
          | where Role.Name == "role"
          | extend RoleMemberAddedTime = TimeGenerated, MemberAddedRoleId = tostring(Role.Value))
          on $left.RoleId == $right.MemberAddedRoleId, InstanceUrl, UserId
      | where RoleMemberAddedTime between (RoleCreateTime .. (RoleCreateTime + role_create_watch_period));
  let remove_role_member_events = DataverseActivity
      | where TimeGenerated >= ago(query_frequency)
      | where Message == "Disassociate" and EntityName == "systemuser"
      | mv-expand Role = Fields
      | where Role.Name == "role"
      | extend ActionTime = TimeGenerated, MemberRemovedRoleId = tostring(Role.Value);
  let role_delete_events = DataverseActivity
      | where TimeGenerated >= ago(query_frequency)
      | where Message == "Delete" and EntityName == "role"
      | extend DeletedRoleID = EntityId, Action = "Role deleted within defined time window"
      | project Action, ActionTime = TimeGenerated, UserId, ClientIp, DeletedRoleID, InstanceUrl;
  let role_member_removals = role_create_add_events
      | join kind=inner (remove_role_member_events) on $left.RoleId == $right.MemberRemovedRoleId
      | where ActionTime between (RoleCreateTime .. (RoleCreateTime + role_create_watch_period))
      | extend Action = "Role membership removed within defined time window";
  let role_deletions = role_create_add_events
      | join kind=inner (role_delete_events) on $left.RoleId == $right.DeletedRoleID
      | where ActionTime between (RoleCreateTime .. (RoleCreateTime + role_create_watch_period));
  union isfuzzy=true role_member_removals, role_deletions
  | extend
      CloudAppId = int(32780),
      AccountName = tostring(split(UserId, '@')[0]),
      UPNSuffix = tostring(split(UserId, '@')[1])
  | project
      UserId,
      InstanceUrl,
      ClientIp,
      Action,
      RoleCreateTime,
      RoleName,
      ActionTime,
      CloudAppId,
      AccountName,
      UPNSuffix
eventGroupingSettings:
  aggregationKind: AlertPerResult
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIp
  - entityType: CloudApplication
    fieldMappings:
      - identifier: AppId
        columnName: CloudAppId
      - identifier: InstanceName
        columnName: InstanceUrl
alertDetailsOverride:
  alertDisplayNameFormat: Dataverse - suspicious role modifications in {{InstanceUrl}}
  alertDescriptionFormat: 'The following action ocurred following role modifications
    changes in {{InstanceUrl}}: {{Action}}.'
  alertSeverityColumnName: Severity
version: 3.2.0

Stages and Predicates

Parameters

let role_create_watch_period = 2d;
let query_frequency = 1h;

Let Binding: remove_role_member_events

let remove_role_member_events = DataverseActivity
    | where TimeGenerated >= ago(query_frequency)
    | where Message == "Disassociate" and EntityName == "systemuser"
    | mv-expand Role = Fields
    | where Role.Name == "role"
    | extend ActionTime = TimeGenerated, MemberRemovedRoleId = tostring(Role.Value);

Let Binding: role_delete_events

let role_delete_events = DataverseActivity
    | where TimeGenerated >= ago(query_frequency)
    | where Message == "Delete" and EntityName == "role"
    | extend DeletedRoleID = EntityId, Action = "Role deleted within defined time window"
    | project Action, ActionTime = TimeGenerated, UserId, ClientIp, DeletedRoleID, InstanceUrl;

Union isfuzzy=true (2 Sources)

Each leg below queries one source; the rule matches if any leg does. Sources: role_member_removals, role_deletions

Leg 1: role_member_removals

Leg 2: role_deletions

Applied to the Combined Result

| extend
    CloudAppId = int(32780),
    AccountName = tostring(split(UserId, '@')[0]),
    UPNSuffix = tostring(split(UserId, '@')[1]) | project
    UserId,
    InstanceUrl,
    ClientIp,
    Action,
    RoleCreateTime,
    RoleName,
    ActionTime,
    CloudAppId,
    AccountName,
    UPNSuffix

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EntityNameeq
  • role
  • systemuser
field:"EntityName" kind:eq
Messageeq
  • Associate
  • Create
  • Delete
  • Disassociate
field:"Message" kind:eq
Role.Nameeq
  • name transforms: kusto_dynamic_path
  • role transforms: kusto_dynamic_path
  • roleid transforms: kusto_dynamic_path
field:"Role.Name" kind:eq

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountNameproject
Actionproject
ActionTimeproject
ClientIpproject
CloudAppIdproject
InstanceUrlproject
RoleCreateTimeproject
RoleNameproject
UPNSuffixproject
UserIdproject

Detect Print Processors Registry Driver Key Creation/Modification

#
Status
available
Severity
medium
Time window
1h
Source
github.com/Azure/Azure-Sentinel

This analytic rule detects any registry value creation or modification of print processor registry Driver key. This will load the executable at startup with print spooler service. This could be an indication of a persistence attempt by an adversary.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: 7edde3d4-9859-4a00-b93c-b19ddda55320
name: Detect Print Processors Registry Driver Key Creation/Modification
description: |
  This analytic rule detects any registry value creation or modification of print processor registry Driver key. This will load the executable at startup with print spooler service. This could be an indication of a persistence attempt by an adversary.
severity: Medium
status: Available 
tags:
  - Schema: _ASim_RegistryEvent
    SchemaVersion: 0.1.2
requiredDataConnectors:
  - connectorId: CrowdStrikeFalconEndpointProtection
    dataTypes: 
      - CommonSecurityLog
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - SecurityAlert
  - connectorId: SentinelOne
    dataTypes:
      - SentinelOne_CL
  - connectorId: VMwareCarbonBlack
    dataTypes:
      - CarbonBlackEvents_CL
  - connectorId: CiscoSecureEndpoint
    dataTypes:
      - CiscoSecureEndpoint_CL
  - connectorId: TrendMicroApexOne
    dataTypes:
      - TMApexOneEvent
  - connectorId: TrendMicroApexOneAma
    dataTypes:
      - TMApexOneEvent
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
  - PrivilegeEscalation
relevantTechniques:
  - T1547
query: |
  // Print Processor Registry Key RegEx
  let printProcessorRegistryRegEx = @'HKEY_LOCAL_MACHINE\\SYSTEM\\[A-Za-z0-9]*ControlSet[A-Za-z0-9]*\\Control\\Print\\Environments\\Windows\s[A-Za-z0-9]+\\Print Processors\\[A-Za-z0-9]+\\Driver';
  _ASim_RegistryEvent
  | where EventType in ('RegistryValueSet', 'RegistryKeyCreated')
  | where RegistryKey matches regex printProcessorRegistryRegEx
  | project
      TimeGenerated,
      DvcHostname,
      ActorUsername,
      ActorUsernameType,
      ActingProcessId,
      ActingProcessName,
      ActingProcessCommandLine,
      RegistryKey,
      RegistryValue,
      RegistryValueType,
      RegistryValueData
  | extend HostName = tostring(split(DvcHostname, '.')[0])
  | extend DnsDomain = tostring(strcat_array(array_slice(split(DvcHostname, '.'), 1, -1), '.'))
  | extend Username = iff(tostring(ActorUsernameType) == 'Windows', tostring(split(ActorUsername, '\\')[1]), ActorUsername)
  | extend NTDomain = iff(tostring(ActorUsernameType) == 'Windows', tostring(split(ActorUsername, '\\')[0]), ActorUsername)
  | extend Username = iff(tostring(ActorUsernameType) == 'UPN', tostring(split(ActorUsername, '@')[0]), Username)
  | extend UPNSuffix = iff(tostring(ActorUsernameType) == 'UPN', tostring(split(ActorUsername, '@')[1]), '')
  | extend RegHive = tostring(split(RegistryKey, '\\')[0]), RegKey = tostring(strcat_array(array_slice(split(RegistryKey, '\\'), 1, -1), '\\')) 
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
      - identifier: NTDomain
        columnName: NTDomain
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Username
      - identifier: UPNSuffix
        columnName: UPNSuffix
      - identifier: NTDomain
        columnName: NTDomain
  - entityType: Process
    fieldMappings:
      - identifier: ProcessId
        columnName: ActingProcessId
      - identifier: CommandLine
        columnName: ActingProcessCommandLine
  - entityType: RegistryKey
    fieldMappings:
      - identifier: Hive
        columnName: RegHive
      - identifier: Key
        columnName: RegKey
  - entityType: RegistryValue
    fieldMappings:
      - identifier: Name
        columnName: RegistryValue
      - identifier: Value
        columnName: RegistryValueData
      - identifier: ValueType
        columnName: RegistryValueType
eventGroupingSettings:
  aggregationKind: SingleAlert
alertDetailsOverride:
  alertDisplayNameFormat: "Print Processor Registry Driver Keys Modified on {{DvcHostname}} ({{DvcIpAddr}}) by ({{ActorUsername}})"
  alertDescriptionFormat: "Process {{ActingProcessName}} ProcessId: ({{ActingProcessId}}) modified registry driver key {{RegistryKey}}."
version: 1.0.0
kind: Scheduled

Stages and Predicates

Let Binding: printProcessorRegistryRegEx used in Stage 3

let printProcessorRegistryRegEx = @'HKEY_LOCAL_MACHINE\\SYSTEM\\[A-Za-z0-9]*ControlSet[A-Za-z0-9]*\\Control\\Print\\Environments\\Windows\s[A-Za-z0-9]+\\Print Processors\\[A-Za-z0-9]+\\Driver';

Stage 1: source

_ASim_RegistryEvent

Stage 2: where

| where EventType in ('RegistryValueSet', 'RegistryKeyCreated')

Stage 3: where

| where RegistryKey matches regex printProcessorRegistryRegEx

Stage 4: project

| project
    TimeGenerated,
    DvcHostname,
    ActorUsername,
    ActorUsernameType,
    ActingProcessId,
    ActingProcessName,
    ActingProcessCommandLine,
    RegistryKey,
    RegistryValue,
    RegistryValueType,
    RegistryValueData

Stage 5: extend (7 consecutive steps)

| extend HostName = tostring(split(DvcHostname, '.')[0])
| extend DnsDomain = tostring(strcat_array(array_slice(split(DvcHostname, '.'), 1, -1), '.'))
| extend Username = iff(tostring(ActorUsernameType) == 'Windows', tostring(split(ActorUsername, '\\')[1]), ActorUsername)
| extend NTDomain = iff(tostring(ActorUsernameType) == 'Windows', tostring(split(ActorUsername, '\\')[0]), ActorUsername)
| extend Username = iff(tostring(ActorUsernameType) == 'UPN', tostring(split(ActorUsername, '@')[0]), Username)
| extend UPNSuffix = iff(tostring(ActorUsernameType) == 'UPN', tostring(split(ActorUsername, '@')[1]), '')
| extend RegHive = tostring(split(RegistryKey, '\\')[0]), RegKey = tostring(strcat_array(array_slice(split(RegistryKey, '\\'), 1, -1), '\\'))

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventTypein
  • RegistryKeyCreated corpus 5 (kusto 5)
  • RegistryValueSet corpus 5 (kusto 5)
field:"EventType" kind:in
RegistryKeyregex_match
  • HKEY_LOCAL_MACHINE\SYSTEM\[A-Za-z0-9]*ControlSet[A-Za-z0-9]*\Control\Print\Environments\Windows\s[A-Za-z0-9]+\Print Processors\[A-Za-z0-9]+\Driver
field:"TargetObject" kind:regex_match value:"HKEY_LOCAL_MACHINE\SYSTEM\[A-Za-z0-9]*ControlSet[A-Za-z0-9]*\Control\Print\Environments\Windows\s[A-Za-z0-9]+\Print Processors\[A-Za-z0-9]+\Driver"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActingProcessCommandLineproject
ActingProcessIdproject
ActingProcessNameproject
ActorUsernameproject
ActorUsernameTypeproject
DvcHostnameproject
RegistryKeyproject
RegistryValueproject
RegistryValueDataproject
RegistryValueTypeproject
TimeGeneratedproject
HostNameextend
DnsDomainextend
Usernameextend
NTDomainextend
UPNSuffixextend
RegHiveextend
RegKeyextend

Guest Users Invited to Tenant by New Inviters

#
Severity
medium
Time window
14d
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

Detects when a Guest User is added by a user account that has not been seen adding a guest in the previous 14 days. Monitoring guest accounts and the access they are provided is important to detect potential account abuse. Accounts added should be investigated to ensure the activity was legitimate. Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#monitoring-for-failed-unusual-sign-ins

MITRE ATT&CK Coverage

TacticTechniques
Persistence

Telemetry Coverage

PlatformRecord / event type
AzureInvite external user

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 572e75ef-5147-49d9-9d65-13f2ed1e3a86
name: Guest Users Invited to Tenant by New Inviters
description: |
  'Detects when a Guest User is added by a user account that has not been seen adding a guest in the previous 14 days.
    Monitoring guest accounts and the access they are provided is important to detect potential account abuse.
    Accounts added should be investigated to ensure the activity was legitimate.
    Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#monitoring-for-failed-unusual-sign-ins'
severity: Medium
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
relevantTechniques:
  - T1078.004
tags:
  - AADSecOpsGuide
query: |
  let inviting_users = (AuditLogs
    | where TimeGenerated between(ago(14d)..ago(1d))
    | where OperationName =~ "Invite external user"
    | where Result =~ "success"
    | extend InitiatingUserPrincipalName = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
    | where isnotempty(InitiatingUserPrincipalName)
    | summarize by InitiatingUserPrincipalName);
    AuditLogs
    | where TimeGenerated > ago(1d)
    | where OperationName =~ "Invite external user"
    | where Result =~ "success"
    | extend InitiatingUserPrincipalName = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
    | extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
    | extend InitiatingIPAddress = tostring(InitiatedBy.user.ipAddress)
    | where isnotempty(InitiatingUserPrincipalName) and InitiatingUserPrincipalName !in (inviting_users)
    | extend TargetUserPrincipalName = tostring(TargetResources[0].userPrincipalName)
    | extend TargetAadUserId = tostring(TargetResources[0].id)
    | extend invitingUser = InitiatingUserPrincipalName, invitedUserPrincipalName = TargetUserPrincipalName
    | extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
    | extend TargetAccountName = tostring(split(TargetUserPrincipalName, "@")[0]), TargetAccountUPNSuffix = tostring(split(TargetUserPrincipalName, "@")[1])
    | project-reorder TimeGenerated, OperationName, Result, TargetUserPrincipalName, TargetAadUserId, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingIPAddress
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: InitiatingUserPrincipalName
      - identifier: Name
        columnName: InitiatingAccountName
      - identifier: UPNSuffix
        columnName: InitiatingAccountUPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: TargetUserPrincipalName
      - identifier: Name
        columnName: TargetAccountName
      - identifier: UPNSuffix
        columnName: TargetAccountUPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: InitiatingAadUserId
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: TargetAadUserId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: InitiatingIPAddress
version: 1.1.1
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Others", "Identity" ]

Stages and Predicates

Let Binding: inviting_users used in Stage 6

let inviting_users = (AuditLogs
  | where TimeGenerated between(ago(14d)..ago(1d))
  | where OperationName =~ "Invite external user"
  | where Result =~ "success"
  | extend InitiatingUserPrincipalName = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
  | where isnotempty(InitiatingUserPrincipalName)
  | summarize by InitiatingUserPrincipalName);

Stage 1: source

AuditLogs

Stage 2: where

| where TimeGenerated > ago(1d)

Stage 3: where

| where OperationName =~ "Invite external user"

Stage 4: where

| where Result =~ "success"

Stage 5: extend (3 consecutive steps)

| extend InitiatingUserPrincipalName = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIPAddress = tostring(InitiatedBy.user.ipAddress)

Stage 6: where

| where isnotempty(InitiatingUserPrincipalName) and InitiatingUserPrincipalName !in (inviting_users)

Stage 7: extend (5 consecutive steps)

| extend TargetUserPrincipalName = tostring(TargetResources[0].userPrincipalName)
| extend TargetAadUserId = tostring(TargetResources[0].id)
| extend invitingUser = InitiatingUserPrincipalName, invitedUserPrincipalName = TargetUserPrincipalName
| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
| extend TargetAccountName = tostring(split(TargetUserPrincipalName, "@")[0]), TargetAccountUPNSuffix = tostring(split(TargetUserPrincipalName, "@")[1])

Stage 8: project-reorder

| project-reorder TimeGenerated, OperationName, Result, TargetUserPrincipalName, TargetAadUserId, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingIPAddress

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
InitiatingUserPrincipalNameeqinviting_usersexcludes:InitiatingUserPrincipalName field:"InitiatingUserPrincipalName" value:"inviting_users"

Output Fields

These fields are emitted when the rule matches.

FieldSource
InitiatingUserPrincipalNameextend
InitiatingAadUserIdextend
InitiatingIPAddressextend
TargetUserPrincipalNameextend
TargetAadUserIdextend
invitedUserPrincipalNameextend
invitingUserextend
InitiatingAccountNameextend
InitiatingAccountUPNSuffixextend
TargetAccountNameextend
TargetAccountUPNSuffixextend

Hunt for Defender for Identity NNR issues

#
Group by
$left.RemoteIP, $right.IPAddress, DeviceName, Joiner, NetworkAddrPrefix, RemotePort
Author
Robbe Van den Daele
Source
github.com/HybridBrothers/Hunting-Queries-Detection-Rules

This query can help you in finding Network Name Resolution health issues of Microsoft Defender for Identity. NNR is a critical component which is used to get more information on IP addresses seen by MDI. Without NNR proparly working, MDI can throw a lot of False Positive alerts.

References

Telemetry Coverage

Rule Body

let networks = DeviceNetworkInfo
    // Expand all IPs
    | mv-expand todynamic(IPAddresses)
    // Get the network address related to the IP
    | extend NetworkAddress = format_ipv4(tostring(IPAddresses.IPAddress), tolong(IPAddresses.SubnetPrefix))
    // Build the IP with the CIDR notation
    | extend IPAddress = strcat(tostring(IPAddresses.IPAddress), "/", tolong(IPAddresses.SubnetPrefix))
    // Save the Prefix as an extra property
    | extend Prefix = tostring(IPAddresses.SubnetPrefix)
    // Make a set of all the IP's belonging to the same subnet
    | summarize make_set(IPAddress) by NetworkAddress, Prefix
    // Count how many IPs there are in one subnet
    | extend CountIPs = array_length(set_IPAddress)
    | extend Joiner = 1;
// Network Information
let network_info = DeviceNetworkInfo
    | mv-expand todynamic(IPAddresses)
    | extend IPAddress = tostring(IPAddresses.IPAddress);
// Ports used in NNR
let nnr_ports = dynamic(["3389", "135", "137"]);
let mdi_servers = dynamic([]);
// Query network connections
DeviceNetworkEvents
// Get events from Defender for Identity sensors - fill in mdi-servers variable for more complete results
| where InitiatingProcessFileName == "Microsoft.Tri.Sensor.exe" or DeviceName has_any (mdi_servers)
// Check traffic for NNR ports
| where RemotePort in (nnr_ports)
// Join the network info for more destination context
| join kind=inner network_info on $left.RemoteIP == $right.IPAddress
// Get distinct values
| project-rename RemoteDeviceName = DeviceName1
| distinct DeviceName, ActionType, RemoteIP, RemotePort, RemoteDeviceName
// Join all network addresses
| extend Joiner = 1
| join kind=inner networks on Joiner
// Check if remote ip is in a certain network address
| extend NetworkAddrPrefix = strcat(NetworkAddress, "/", Prefix)
| where ipv4_is_in_range(RemoteIP, NetworkAddrPrefix)
// Create Object to reuse later
| extend Obj = pack(
    "DeviceName", DeviceName,
    "NetworkAddrPrefix", NetworkAddrPrefix,
    "RemotePort", RemotePort,
    "RemoteIP", RemoteIP
)
// Count amount of failed and succeeded logins
| summarize FailedConnections = countif(ActionType == "ConnectionFailed"), 
    SucceededConnections = countif(ActionType == "ConnectionSuccess") by tostring(Obj)
// Extract the columns from the object again
| extend Obj = todynamic(Obj)
// Save the properties for later use
| extend DeviceName = tostring(Obj.DeviceName),
    NetworkAddrPrefix = tostring(Obj.NetworkAddrPrefix),
    RemotePort = tostring(Obj.RemotePort),
    RemoteIP = tostring(Obj.RemoteIP)
// Create a new object to save the amount of failed and succeeded attempts per IP
| extend Obj = pack(
    "RemoteIP", RemoteIP,
    "SucceededConnections", SucceededConnections,
    "FailedConnections", FailedConnections
)
// Create a list of the remote ips and their connections by MDI sensor, destination subnet and RemoteIP
// Subnets with only fails on both ports will fail in NNR
| summarize ConnectionDetails = make_set(Obj), 
    TotalSucceededConnections = sum(SucceededConnections), 
    TotalFailedConnections = sum(FailedConnections) by DeviceName, NetworkAddrPrefix, RemotePort
// Filter out /32 addresses
| where NetworkAddrPrefix !contains "/32"
// Sorting
| sort by TotalFailedConnections desc
// Reorder
| project-reorder DeviceName, NetworkAddrPrefix, RemotePort, TotalSucceededConnections, TotalFailedConnections, ConnectionDetails

Stages and Predicates

Parameters

let nnr_ports = dynamic(["3389", "135", "137"]);
let mdi_servers = dynamic([]);

Let Binding: networks used in Stages 1, 10

let networks = DeviceNetworkInfo
    | mv-expand todynamic(IPAddresses)
    | extend NetworkAddress = format_ipv4(tostring(IPAddresses.IPAddress), tolong(IPAddresses.SubnetPrefix))
    | extend IPAddress = strcat(tostring(IPAddresses.IPAddress), "/", tolong(IPAddresses.SubnetPrefix))
    | extend Prefix = tostring(IPAddresses.SubnetPrefix)
    | summarize make_set(IPAddress) by NetworkAddress, Prefix
    | extend CountIPs = array_length(set_IPAddress)
    | extend Joiner = 1;

Let Binding: network_info used in Stages 2, 6

let network_info = DeviceNetworkInfo
    | mv-expand todynamic(IPAddresses)
    | extend IPAddress = tostring(IPAddresses.IPAddress);

Stage 1: source

let networks

Stage 2: source

let network_info

Stage 3: source

DeviceNetworkEvents

Stage 4: where

where (DeviceName has "[]" or InitiatingProcessFileName == "Microsoft.Tri.Sensor.exe")

Stage 5: where

where RemotePort in (135, 137, 3389)

Stage 6: join

join kind=inner (network_info) on $left.RemoteIP, $right.IPAddress

Stage 7: project-rename

project-rename

Stage 8: distinct

distinct ActionType, DeviceName, RemoteDeviceName, RemoteIP, RemotePort

Stage 9: extend

extend Joiner

Stage 10: join

join kind=inner (networks) on Joiner

Stage 11: extend

extend NetworkAddrPrefix

Stage 12: where

where /* macro: ipv4_is_in_range(RemoteIP, NetworkAddrPrefix) */

Stage 13: extend

extend Obj

Stage 14: summarize

summarize FailedConnections, SucceededConnections

Stage 15: extend (3 consecutive steps)

extend DeviceName, NetworkAddrPrefix, Obj, RemoteIP, RemotePort

Stage 16: summarize

summarize ConnectionDetails, TotalFailedConnections, TotalSucceededConnections by DeviceName, NetworkAddrPrefix, RemotePort

Stage 17: where

where not (NetworkAddrPrefix contains "/32")

Stage 18: sort

sort by TotalFailedConnections

Stage 19: project-reorder

project-reorder

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
NetworkAddrPrefixcontains/32excludes:NetworkAddrPrefix field:"NetworkAddrPrefix" value:"/32"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ConnectionDetailssummarize
DeviceNamesummarize
NetworkAddrPrefixsummarize
RemotePortsummarize
TotalFailedConnectionssummarize
TotalSucceededConnectionssummarize

Modification of Accessibility Features

#
Severity
medium
Time window
1h
Group by
CommandLine, Computer, CurrentDirectory, Description, EventID, Hashes, Image, OriginalFileName, ParentCommandLine, ParentImage, ParentProcessGuid, ParentUser, ProcessGuid, ProcessId, User
Author
Vasileios Paschalidis
Source
github.com/Azure/Azure-Sentinel

Adversaries may establish persistence and/or elevate privileges by executing malicious content triggered by accessibility features. Windows contains accessibility features that may be launched with a key combination before a user has logged in (ex: when the user is on the Windows logon screen). An adversary can modify the way these programs are launched to get a command prompt or backdoor without logging in to the system. Two common accessibility programs are C:\Windows\System32\sethc.exe, launched when the shift key is pressed five times and C:\Windows\System32\utilman.exe, launched when the Windows + U key combination is pressed. The sethc.exe program is often referred to as "sticky keys", and has been used by adversaries for unauthenticated access through a remote desktop login screen. [1] Ref: https://attack.mitre.org/techniques/T1546/008/

MITRE ATT&CK Coverage

Telemetry Coverage

ProviderRecord / event type
SysmonEvent ID 1: Process creation

Rule Body

id: d714ef62-1a56-4779-804f-91c4158e528d
name: Modification of Accessibility Features
description: |
  'Adversaries may establish persistence and/or elevate privileges by executing malicious content triggered by accessibility features. Windows contains accessibility features that may be launched with a key combination before a user has logged in (ex: when the user is on the Windows logon screen). An adversary can modify the way these programs are launched to get a command prompt or backdoor without logging in to the system.
  Two common accessibility programs are C:\Windows\System32\sethc.exe, launched when the shift key is pressed five times and C:\Windows\System32\utilman.exe, launched when the Windows + U key combination is pressed. The sethc.exe program is often referred to as "sticky keys", and has been used by adversaries for unauthenticated access through a remote desktop login screen. [1]
  Ref: https://attack.mitre.org/techniques/T1546/008/'
severity: Medium
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
relevantTechniques:
  - T1546.008
query: |
  let ImagesList = dynamic (["sethc.exe","utilman.exe","osk.exe","Magnify.exe","Narrator.exe","DisplaySwitch.exe","AtBroker.exe"]); 
  let OriginalFileNameList = dynamic (["sethc.exe","utilman.exe","osk.exe","Magnify.exe","Narrator.exe","DisplaySwitch.exe","AtBroker.exe","SR.exe","utilman2.exe","ScreenMagnifier.exe"]); 
  Event
  | where EventLog == "Microsoft-Windows-Sysmon/Operational" and EventID==1
  | parse EventData with * 'ProcessId">' ProcessId "<" * 'Image">' Image "<" * 'OriginalFileName">' OriginalFileName "<" *
  | where Image has_any (ImagesList) and not (OriginalFileName has_any (OriginalFileNameList))
  | parse EventData with * 'ProcessGuid">' ProcessGuid "<" * 'Description">' Description "<" * 'CommandLine">' CommandLine "<" * 'CurrentDirectory">' CurrentDirectory "<" * 'User">' User "<" * 'LogonGuid">' LogonGuid "<" * 'Hashes">' Hashes "<" * 'ParentProcessGuid">' ParentProcessGuid "<" * 'ParentImage">' ParentImage "<" * 'ParentCommandLine">' ParentCommandLine "<" * 'ParentUser">' ParentUser "<" *
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by EventID, Computer, User, ParentImage, ParentProcessGuid, ParentCommandLine, ParentUser, Image, ProcessId, ProcessGuid, CommandLine, Description, OriginalFileName, CurrentDirectory, Hashes
  | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
  | extend AccountName = tostring(split(User, "\\")[1]), AccountNTDomain = tostring(split(User, "\\")[0])
  | extend ImageFileName = tostring(split(Image, "\\")[-1])
  | extend ImageDirectory = replace_string(Image, ImageFileName, "")
  | project-away DomainIndex
entityMappings:
  - entityType: Process
    fieldMappings:
      - identifier: CommandLine
        columnName: CommandLine
      - identifier: ProcessId
        columnName: ProcessId
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: HostNameDomain
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: User
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountNTDomain
  - entityType: File
    fieldMappings:
      - identifier: Name
        columnName: ImageFileName
      - identifier: Directory
        columnName: ImageDirectory
version: 1.0.4
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Vasileios Paschalidis
    support:
        tier: Community
    categories:
        domains: [ "Security - Others" ]

Stages and Predicates

Parameters

let ImagesList = dynamic (["sethc.exe","utilman.exe","osk.exe","Magnify.exe","Narrator.exe","DisplaySwitch.exe","AtBroker.exe"]);

Let Binding: OriginalFileNameList used in Stage 4

let OriginalFileNameList = dynamic (["sethc.exe","utilman.exe","osk.exe","Magnify.exe","Narrator.exe","DisplaySwitch.exe","AtBroker.exe","SR.exe","utilman2.exe","ScreenMagnifier.exe"]);

Stage 1: source

Event

Stage 2: where

| where EventLog == "Microsoft-Windows-Sysmon/Operational" and EventID==1

Stage 3: parse

| parse EventData with * 'ProcessId">' ProcessId "<" * 'Image">' Image "<" * 'OriginalFileName">' OriginalFileName "<" *

Stage 4: where

| where Image has_any (ImagesList) and not (OriginalFileName has_any (OriginalFileNameList))

Stage 5: parse

| parse EventData with * 'ProcessGuid">' ProcessGuid "<" * 'Description">' Description "<" * 'CommandLine">' CommandLine "<" * 'CurrentDirectory">' CurrentDirectory "<" * 'User">' User "<" * 'LogonGuid">' LogonGuid "<" * 'Hashes">' Hashes "<" * 'ParentProcessGuid">' ParentProcessGuid "<" * 'ParentImage">' ParentImage "<" * 'ParentCommandLine">' ParentCommandLine "<" * 'ParentUser">' ParentUser "<" *

Stage 6: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by EventID, Computer, User, ParentImage, ParentProcessGuid, ParentCommandLine, ParentUser, Image, ProcessId, ProcessGuid, CommandLine, Description, OriginalFileName, CurrentDirectory, Hashes

Stage 7: extend (5 consecutive steps)

| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
| extend AccountName = tostring(split(User, "\\")[1]), AccountNTDomain = tostring(split(User, "\\")[0])
| extend ImageFileName = tostring(split(Image, "\\")[-1])
| extend ImageDirectory = replace_string(Image, ImageFileName, "")

Stage 8: project-away

| project-away DomainIndex

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventIDeq
  • 1 corpus 12 (kusto 11, elastic 1)
field:"EventID" kind:eq value:"1"
EventLogeq
  • Microsoft-Windows-Sysmon/Operational corpus 3 (kusto 3)
field:"EventLog" kind:eq value:"Microsoft-Windows-Sysmon/Operational"
Imagematch
  • AtBroker.exe transforms: term
  • DisplaySwitch.exe transforms: term
  • Magnify.exe transforms: term
  • Narrator.exe transforms: term
  • osk.exe transforms: term
  • sethc.exe transforms: term
  • utilman.exe transforms: term
field:"Image" kind:match

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
OriginalFileNamematchsethc.exe, utilman.exe, osk.exe, Magnify.exe, Narrator.exe, DisplaySwitch.exe, AtBroker.exe, SR.exe, utilman2.exe, ScreenMagnifier.exeexcludes:OriginalFileName

Output Fields

These fields are emitted when the rule matches.

FieldSource
CommandLinesummarize
Computersummarize
CurrentDirectorysummarize
Descriptionsummarize
EndTimesummarize
EventIDsummarize
Hashessummarize
Imagesummarize
OriginalFileNamesummarize
ParentCommandLinesummarize
ParentImagesummarize
ParentProcessGuidsummarize
ParentUsersummarize
ProcessGuidsummarize
ProcessIdsummarize
StartTimesummarize
Usersummarize
HostNameextend
HostNameDomainextend
AccountNTDomainextend
AccountNameextend
ImageFileNameextend
ImageDirectoryextend

Pathlock TDnR - User Access Management Password Resets

#
Status
available
Severity
medium
Time window
1h
Source
github.com/Azure/Azure-Sentinel

Detects password reset events from the Pathlock User Access Management module in SAP, forwarded to Microsoft Sentinel. Unexpected or bulk password resets may indicate account takeover preparation, credential stuffing followup, or unauthorized use of administrative password reset capabilities.

MITRE ATT&CK Coverage

Rule Body

id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c53
name: Pathlock TDnR - User Access Management Password Resets
kind: Scheduled
description: >-
  Detects password reset events from the Pathlock User Access Management module in SAP,
  forwarded to Microsoft Sentinel. Unexpected or bulk password resets may indicate account
  takeover preparation, credential stuffing followup, or unauthorized use of administrative
  password reset capabilities.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Pathlock_TDnR
    dataTypes:
      - Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
  - Persistence
relevantTechniques:
  - T1098
  - T1078
query: |
  Pathlock_TDnR_CL
  | where DataSource == "SAST_UAM_PWR"
  | project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
            Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
            MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Bname
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: Hostname
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIp
eventGroupingSettings:
  aggregationKind: SingleAlert
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 5h
    matchingMethod: AnyAlert
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.0

Stages and Predicates

Stage 1: source

Pathlock_TDnR_CL

Stage 2: where

| where DataSource == "SAST_UAM_PWR"

Stage 3: project

| project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
          Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
          MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AffectedUserproject
Areaproject
Bnameproject
CentralTsproject
DataSourceproject
DestIpproject
Eventidproject
Hostnameproject
Instanceproject
LogLineproject
MessageV1project
MessageV2project
MessageV3project
MessageV4project
MsgIdproject
MsgNoproject
MsgTypeproject
Reportproject
SrcIpproject
Subidproject
Sysidproject
Tcodeproject
TimeGeneratedproject

Ransom Protect Detected a Ransomware Attack

#
Status
available
Severity
high
Source
github.com/Azure/Azure-Sentinel

Monitors CTERA platform to detect potential ransomware attacks detected by CTERA Ransom Protect AI engine.

MITRE ATT&CK Coverage

TacticTechniques
Impact

Rule Body

id: 7a075edf-1cf2-4038-ba9c-c354db6409de
name: Ransom Protect Detected a Ransomware Attack
description: 'Monitors CTERA platform to detect potential ransomware attacks detected by CTERA Ransom Protect AI engine.'
severity: High
status: Available
kind: NRT                    # Set to NRT for near real-time alerting
requiredDataConnectors:
  - connectorId: CTERA
    dataTypes:
      - Syslog
tactics:
  - Impact
relevantTechniques:
  - T1486
query: |
    Syslog
    | where SyslogMessage contains "Ransomware incident detected"
    | extend 
    Portal = extract("portal:(\\w+)", 1, SyslogMessage),
    EdgeFiler = extract("Edge Filer:(\\w+-\\d+)", 1, SyslogMessage),
    IP = extract("\\(IP:([0-9.]+)\\)", 1, SyslogMessage),
    User = extract("user:(\\w+)", 1, SyslogMessage),
    IncidentType = extract("Incident type:(\\w+)", 1, SyslogMessage),
    StartTime = extract("started at \"([^\"]+)\"", 1, SyslogMessage),
    EndTime = extract("ended at \"([^\"]+)\"", 1, SyslogMessage)
    | project TimeGenerated, Portal, EdgeFiler, IP, User, IncidentType, StartTime, EndTime
suppressionDuration: PT5H
suppressionEnabled: false
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: SingleAlert
alertDetailsOverride:
  alertnameFormat: 'CTERA Ransom Protect Detected a Ransomware Attack.'
  alertDescriptionFormat: CTERA Ransom Protect Detected a Ransomware Attack at {{TimeGenerated}}.
customDetails:
  EdgeFiler: EdgeFiler
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: EdgeFiler
version: 1.0.2

Stages and Predicates

Stage 1: source

Syslog

Stage 2: where

| where SyslogMessage contains "Ransomware incident detected"

Stage 3: extend

| extend 
Portal = extract("portal:(\\w+)", 1, SyslogMessage),
EdgeFiler = extract("Edge Filer:(\\w+-\\d+)", 1, SyslogMessage),
IP = extract("\\(IP:([0-9.]+)\\)", 1, SyslogMessage),
User = extract("user:(\\w+)", 1, SyslogMessage),
IncidentType = extract("Incident type:(\\w+)", 1, SyslogMessage),
StartTime = extract("started at \"([^\"]+)\"", 1, SyslogMessage),
EndTime = extract("ended at \"([^\"]+)\"", 1, SyslogMessage)

Stage 4: project

| project TimeGenerated, Portal, EdgeFiler, IP, User, IncidentType, StartTime, EndTime

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
SyslogMessagecontains
  • Ransomware incident detected
field:"SyslogMessage" kind:contains value:"Ransomware incident detected"

Output Fields

These fields are emitted when the rule matches.

FieldSource
EdgeFilerproject
EndTimeproject
IPproject
IncidentTypeproject
Portalproject
StartTimeproject
TimeGeneratedproject
Userproject

SailPointIdentityNowFailedEvents

#
Status
available
Severity
high
Time window
14d
Source
github.com/Azure/Azure-Sentinel

'Detects all events with status failed.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: c3835197-fd07-447e-a0ac-7540d51a1f64
name: SailPointIdentityNowFailedEvents
description: |
  'Detects all events with status failed.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: SailPointIdentityNow
    dataTypes:
      - SailPointIDN_Events
  - connectorId: SailPointIdentityNowConnector
    dataTypes:
      - SailPointIDN_Events
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1133
query: |
  declare query_parameters(lbperiod:timespan = 14d);
    SailPointIDN_Events
    | where TimeGenerated > ago(lbperiod) and Status == "FAILED"
    | project TechnicalName, Operation, EventType
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: TechnicalName
version: 1.1.0
kind: Scheduled

Stages and Predicates

Stage 1: source

declare query_parameters(lbperiod:timespan = 14d);
  SailPointIDN_Events

Stage 2: where

| where TimeGenerated > ago(lbperiod) and Status == "FAILED"

Stage 3: project

| project TechnicalName, Operation, EventType

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Statuseq
  • FAILED
field:"Status" kind:eq value:"FAILED"

Output Fields

These fields are emitted when the rule matches.

FieldSource
EventTypeproject
Operationproject
TechnicalNameproject

Tenable.ad Golden Ticket

#
Severity
high
Time window
2h
Source
github.com/Azure/Azure-Sentinel

'Searches for Golden Ticket attacks.'

MITRE ATT&CK Coverage

Rule Body

id: d1abda25-f88a-429a-8163-582533cd0def
name: Tenable.ad Golden Ticket
description: |
    'Searches for Golden Ticket attacks.'
severity: High
requiredDataConnectors:
  - connectorId: Tenable.ad
    dataTypes:
      - Tenable_ad_CL
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
relevantTechniques:
  - T1558.001
query: |
  // For the query to work properly, make sure you have imported the afad_parser.kql parser into the workspace
  // Retrieve the parser here: https://raw.githubusercontent.com/tenable/Azure-Sentinel/Tenable.ad-connector/Solutions/TenableAD/Parsers/afad_parser.kql
  // Then, create the Kusto Function with alias afad_parser
  afad_parser
    | where MessageType == 2 and Codename == "Golden Ticket"
    | extend HostName = tostring(split(Host, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Host, '.'), 1, -1), '.'))
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
version: 1.0.1
kind: Scheduled

Stages and Predicates

Stage 1: source

afad_parser

Stage 2: where

| where MessageType == 2 and Codename == "Golden Ticket"

Stage 3: extend

| extend HostName = tostring(split(Host, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Host, '.'), 1, -1), '.'))

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Codenameeq
  • Golden Ticket corpus 3 (kusto 3)
field:"Codename" kind:eq value:"Golden Ticket"
MessageTypeeq
  • 2 corpus 21 (kusto 21)
field:"MessageType" kind:eq value:"2"

Output Fields

These fields are emitted when the rule matches.

FieldSource
DnsDomainextend
HostNameextend

Tenable.ad Password issues

#
Severity
low
Time window
2h
Group by
Severity
Source
github.com/Azure/Azure-Sentinel

'Searches for triggered Indicators of Exposures related to password issues.'

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Rule Body

id: 2518b57f-1a8b-44ea-935d-7dc1cfe4f918
name: Tenable.ad Password issues
description: |
    'Searches for triggered Indicators of Exposures related to password issues.'
severity: Low
requiredDataConnectors:
  - connectorId: Tenable.ad
    dataTypes:
      - Tenable_ad_CL
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
relevantTechniques:
  - T1110
query: |
  // For the query to work properly, make sure you have imported the afad_parser.kql parser into the workspace
  // Retrieve the parser here: https://raw.githubusercontent.com/tenable/Azure-Sentinel/Tenable.ad-connector/Solutions/TenableAD/Parsers/afad_parser.kql
  // Then, create the Kusto Function with alias afad_parser
  let SeverityTable=datatable(Severity:string,Level:int) [
  "low", 1,
  "medium", 2,
  "high", 3,
  "critical", 4
  ];
  let codeNameList = datatable(Codename:string)["C-CLEARTEXT-PASSWORD", "C-PASSWORD-DONT-EXPIRE", "C-USER-REVER-PWDS", "C-PASSWORD-POLICY", "C-USER-PASSWORD", "C-KRBTGT-PASSWORD", "C-AAD-SSO-PASSWORD", "C-REVER-PWD-GPO"];
  afad_parser
  | where MessageType == 0 and Codename in~ (codeNameList)
  | lookup kind=leftouter SeverityTable on Severity
  | order by Level
  | extend HostName = tostring(split(Host, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Host, '.'), 1, -1), '.'))
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
version: 1.0.1
kind: Scheduled

Stages and Predicates

Let Binding: SeverityTable used in Stage 3

let SeverityTable = datatable(Severity:string,Level:int) [
"low", 1,
"medium", 2,
"high", 3,
"critical", 4
];

Let Binding: codeNameList used in Stage 2

let codeNameList = datatable(Codename:string)["C-CLEARTEXT-PASSWORD", "C-PASSWORD-DONT-EXPIRE", "C-USER-REVER-PWDS", "C-PASSWORD-POLICY", "C-USER-PASSWORD", "C-KRBTGT-PASSWORD", "C-AAD-SSO-PASSWORD", "C-REVER-PWD-GPO"];

Stage 1: source

afad_parser

Stage 2: where

| where MessageType == 0 and Codename in~ (codeNameList)

Stage 3: kusto:lookup

lookup kind=leftouter (SeverityTable) on Severity

Stage 4: sort

sort by Level

Stage 5: extend

extend DnsDomain, HostName

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DnsDomainextend
HostNameextend

Back to all Kusto rules