Detection rules › Kusto

CTE - Risky Device (Multiple IOC URL Hits)

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

A single device has repeatedly hit known-malicious (threat-intel-matched) URLs, indicating persistent compromise risk or repeated user exposure.

MITRE ATT&CK coverage

TacticTechniques
Initial Access
Command & Control

Rule body

id: e1c0f32d-a57b-42ae-8038-b869bca54fef
name: CTE - Risky Device (Multiple IOC URL Hits)
description: |
  A single device has repeatedly hit known-malicious (threat-intel-matched) URLs, indicating persistent compromise risk or repeated user exposure.
severity: Medium
requiredDataConnectors:
  - connectorId: cybereinforce_cte
    dataTypes:
      - CybereinforceCTE_CL
queryFrequency: 1h
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - CommandAndControl
relevantTechniques:
  - T1189
  - T1071
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)
  | where RuleName == "Cybereinforce Threat Intelligence" or IsIoc == true
  | summarize IOC_Hits=count(), BlockedUrls=make_set(Url, 50), ClientIps=make_set(ClientIp, 20), LastSeen=max(TimeGenerated) by DeviceId, HostName, DeviceUser, DeviceOs, TenantId
  | where IOC_Hits >= 2
  | extend Url = tostring(BlockedUrls[0]), ClientIp = tostring(ClientIps[0])
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Url
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIp
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 =
if/* macro: isnotempty(tostring(d.hostname)) */tostring(d.hostname)
elsestrcat("cte-device-", substring(EntityId, 0, 8))
RuleName =
if/* macro: isnotempty(tostring(d.ruleName)) */tostring(d.ruleName)
else"Pattern Match"

Stage 5: where

| where RuleName == "Cybereinforce Threat Intelligence" or IsIoc == true

Stage 6: summarize

| summarize IOC_Hits=count(), BlockedUrls=make_set(Url, 50), ClientIps=make_set(ClientIp, 20), LastSeen=max(TimeGenerated) by DeviceId, HostName, DeviceUser, DeviceOs, TenantId

Stage 7: where

| where IOC_Hits >= 2

Stage 8: extend

| extend Url = tostring(BlockedUrls[0]), ClientIp = tostring(ClientIps[0])

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
BlockedUrlssummarize
ClientIpssummarize
DeviceIdsummarize
DeviceOssummarize
DeviceUsersummarize
HostNamesummarize
IOC_Hitssummarize
LastSeensummarize
TenantIdsummarize
ClientIpextend
Urlextend