Detection rules › Kusto

Kusto rules: 1g

App GW WAF - Path Traversal Attack

#
Status
available
Severity
high
Time window
6h
Group by
Action, ClientIp, TransactionId, Uri
Source
github.com/Azure/Azure-Sentinel

'Identifies a match for a Path Traversal based attack in the App Gateway WAF logs. The threshold value in the query can be changed as per your infrastructure's requirements. References: https://owasp.org/www-community/attacks/Path_Traversal'

MITRE ATT&CK Coverage

Rule Body

id: b6c3a8a6-d22c-4882-9c57-abc01690938b
name: App GW WAF - Path Traversal Attack 
description: | 
  'Identifies a match for a Path Traversal based attack in the App Gateway WAF logs. The threshold value in the query can be changed as per your infrastructure's requirements.
  References: https://owasp.org/www-community/attacks/Path_Traversal'
severity: High
status: Available 
requiredDataConnectors: 
  - connectorId: WAF
    dataTypes: 
      - AzureDiagnostics
queryFrequency: 6h 
queryPeriod: 6h 
triggerOperator: gt 
triggerThreshold: 0 
tactics: 
  - DefenseEvasion
  - Execution
  - InitialAccess
  - PrivilegeEscalation
  - Discovery
relevantTechniques:
  - T1548
  - T1203
  - T1190
  - T1548
  - T1087
tags:
  - Path Traversal
query:  | 
 let Threshold = 3;  
 AGWFirewallLogs
 | where Action == "Matched"
 | where Message has "Path Traversal Attack"
 | project TransactionId, Hostname, RequestUri, TimeGenerated, ClientIp, Message, DetailedMessage, DetailedData
 | join kind = inner(
 AGWFirewallLogs
 | where Action == "Blocked") on TransactionId
 | extend Uri = strcat(Hostname,RequestUri)
 | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), TransactionID = make_set(TransactionId,100), Message = make_set(Message,100), Detail_Message = make_set(DetailedMessage,100), Detail_Data = make_set(DetailedData,100), Total_TransactionId = dcount(TransactionId) by ClientIp, Uri, Action
 | where Total_TransactionId >= Threshold
# The Threshold value above can be changed as per your infrastructure's requirement
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Uri
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIp 
version: 1.0.3
kind: Scheduled

Stages and Predicates

Parameters

let Threshold = 3;

Stage 1: source

AGWFirewallLogs

Stage 2: where

| where Action == "Matched"

Stage 3: where

| where Message has "Path Traversal Attack"

Stage 4: project

| project TransactionId, Hostname, RequestUri, TimeGenerated, ClientIp, Message, DetailedMessage, DetailedData

Stage 5: join

| join kind = inner(
AGWFirewallLogs
| where Action == "Blocked") on TransactionId

Stage 6: extend

extend Uri

Stage 7: summarize

summarize Detail_Data, Detail_Message, EndTime, Message, StartTime, Total_TransactionId, TransactionID by ClientIp, Uri, Action

Stage 8: where

where Total_TransactionId >= 3

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Actionsummarize
ClientIpsummarize
Detail_Datasummarize
Detail_Messagesummarize
EndTimesummarize
Messagesummarize
StartTimesummarize
Total_TransactionIdsummarize
TransactionIDsummarize
Urisummarize

AWSCloudTrail - Privilege escalation via CRUD IAM policy

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

Detects creation of inline IAM policies that grant broad IAM create, read, update, and delete capabilities. This behavior can establish or expand privileged control paths in the account and should be reviewed as potential privilege escalation.

MITRE ATT&CK Coverage

TacticTechniques
Privilege Escalation

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: e20d35a3-4fec-4c8b-81b1-fc33b41990b0
name: AWSCloudTrail - Privilege escalation via CRUD IAM policy
description: |
  Detects creation of inline IAM policies that grant broad IAM create, read, update, and delete capabilities.
  This behavior can establish or expand privileged control paths in the account and should be reviewed as potential
  privilege escalation.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: AWS
    dataTypes:
      - AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - PrivilegeEscalation
relevantTechniques:
  - T1098.003
query: |
    AWSCloudTrail
      | where EventName in ("PutUserPolicy","PutRolePolicy","PutGroupPolicy") and isempty(ErrorCode) and isempty(ErrorMessage)
      | extend PolicyName = tostring(parse_json(RequestParameters).policyName)
      | extend Statement = parse_json(tostring((parse_json(RequestParameters).policyDocument))).Statement
      | mvexpand Statement
      | extend Action = parse_json(Statement).Action , Effect = tostring(parse_json(Statement).Effect), Resource = tostring(parse_json(Statement).Resource), Condition = tostring(parse_json(Statement).Condition)
      | extend Action = tostring(Action)
      | where Effect =~ "Allow" and (Action contains "iam:Create" and (Action contains "iam:Get" or Action contains "iam:List")  and Action contains "iam:Update" and Action contains "iam:Delete") and Resource == "*" and Condition == ""
      | extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
      | extend UserName = tostring(split(UserIdentityArn, '/')[-1])
      | extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
      | extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
        AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
      | distinct TimeGenerated, EventName, PolicyName, SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityArn, UserIdentityUserName
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
      - identifier: CloudAppAccountId
        columnName: RecipientAccountId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIpAddress
customDetails:
  PolicyName: PolicyName
  EventName: EventName
  UserIdentityArn: UserIdentityArn
  RecipientAccountId: RecipientAccountId
alertDetailsOverride:
  alertDisplayNameFormat: 'AWS IAM privilege escalation policy update by {{AccountName}}'
  alertDescriptionFormat: 'Detected {{EventName}} Event, updating inline IAM escalation policy {{PolicyName}} in account {{RecipientAccountId}}.'
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

AWSCloudTrail

Stage 2: where

| where EventName in ("PutUserPolicy","PutRolePolicy","PutGroupPolicy") and isempty(ErrorCode) and isempty(ErrorMessage)

Stage 3: extend

| extend PolicyName = tostring(parse_json(RequestParameters).policyName)

Stage 4: extend

| extend Statement = parse_json(tostring((parse_json(RequestParameters).policyDocument))).Statement

Stage 5: mv-expand

| mvexpand Statement

Stage 6: extend

| extend Action = parse_json(Statement).Action , Effect = tostring(parse_json(Statement).Effect), Resource = tostring(parse_json(Statement).Resource), Condition = tostring(parse_json(Statement).Condition)

Stage 7: extend

| extend Action = tostring(Action)

Stage 8: where

| where Effect =~ "Allow" and (Action contains "iam:Create" and (Action contains "iam:Get" or Action contains "iam:List")  and Action contains "iam:Update" and Action contains "iam:Delete") and Resource == "*" and Condition == ""

Stage 9: extend (4 consecutive steps)

| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
    AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")

Stage 10: distinct

| distinct TimeGenerated, EventName, PolicyName, SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityArn, UserIdentityUserName

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Actioncontains
  • iam:Create
  • iam:Delete
  • iam:Get
  • iam:List
  • iam:Update
field:"Action" kind:contains
Effecteq
  • Allow
field:"Effect" kind:eq value:"Allow"
ErrorCodeis_null
  • (no value, null check)
field:"aws::errorCode" kind:is_null
ErrorMessageis_null
  • (no value, null check)
field:"aws::errorMessage" kind:is_null
EventNamein
  • PutGroupPolicy corpus 12 (kusto 11, panther 1)
  • PutRolePolicy corpus 12 (kusto 11, panther 1)
  • PutUserPolicy corpus 12 (kusto 11, panther 1)
field:"aws::eventName" kind:in
Resourceeq
  • *
field:"Resource" kind:eq value:"*"

Output Fields

These fields are emitted when the rule matches.

FieldSource
PolicyNameextend
Statementextend
Actionextend
Conditionextend
Effectextend
Resourceextend
UserIdentityArnextend
UserNameextend
AccountNameextend
AccountUPNSuffixextend

BloodHound Attack Path Finding - WriteGpLink Privileges on Tier Zero Objects

#

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
medium
Time window
1h
Source
github.com/Azure/Azure-Sentinel

BloodHound Attack Path Finding - WriteGpLink Privileges on Tier Zero Objects

Rule Body

id: b64ee057-94a4-42a5-a769-430a2943f764
name: BloodHound Attack Path Finding - WriteGpLink Privileges on Tier Zero Objects
description: BloodHound Attack Path Finding - WriteGpLink Privileges on Tier Zero
  Objects
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: BloodHoundEnterprise
    dataTypes:
      - BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: BHEAttackPathsData_CL | where Finding == "T0WriteGPLink"
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: domain_name
customDetails:
  Description: ShortDescription
  Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

BHEAttackPathsData_CL

Stage 2: where

| where Finding == "T0WriteGPLink"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Findingeq
  • T0WriteGPLink
field:"Finding" kind:eq value:"T0WriteGPLink"

Cisco WSA - Multiple attempts to download unwanted file

#
Status
available
Severity
medium
Time window
15m
Group by
UrlOriginal
Source
github.com/Azure/Azure-Sentinel

'Detects when multiple attempts to download unwanted file occur.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 46b6c6fc-2c1a-4270-be10-9d444d83f027
name: Cisco WSA - Multiple attempts to download unwanted file
description: |
  'Detects when multiple attempts to download unwanted file occur.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1189
query: |
  let threshold = 2;
  CiscoWSAEvent
  | where DvcAction =~ 'BLOCK_ADMIN_FILE_TYPE'
  | summarize i_src = makeset(SrcIpAddr) by UrlOriginal, bin(TimeGenerated, 15m)
  | where array_length(i_src) >= threshold
  | extend IPCustomEntity = i_src, UrlCustomEntity = UrlOriginal
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: UrlCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 2;

Stage 1: source

CiscoWSAEvent

Stage 2: where

| where DvcAction =~ 'BLOCK_ADMIN_FILE_TYPE'

Stage 3: summarize

| summarize i_src = makeset(SrcIpAddr) by UrlOriginal, bin(TimeGenerated, 15m)

Stage 4: where

| where array_length(i_src) >= threshold

Stage 5: extend

| extend IPCustomEntity = i_src, UrlCustomEntity = UrlOriginal

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
DvcActioneq
  • BLOCK_ADMIN_FILE_TYPE
field:"DvcAction" kind:eq value:"BLOCK_ADMIN_FILE_TYPE"

Output Fields

These fields are emitted when the rule matches.

FieldSource
UrlOriginalsummarize
i_srcsummarize
IPCustomEntityextend
UrlCustomEntityextend

Configuration Backup Job Failed

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

Detects failed configuration backup operations. This might indicate system or storage issues, or a potential sabotage of the backup infrastructure.

Rule Body

id: 18833d01-703a-438d-8ac6-84faa82e9b52
name: Configuration Backup Job Failed
description: Detects failed configuration backup operations. This might indicate
  system or storage issues, or a potential sabotage of the backup infrastructure.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: SyslogAma
  dataTypes:
  - Syslog
queryFrequency: 3h
queryPeriod: 3h
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetFinishedConfigurationBackupSessions\r\n| where Result == \"Failed\"\
  \r\n| project\r\n    Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\r\
  \n    DataSource = Computer,\r\n    EventId = instanceId,\r\n    UserName = user,\r\
  \n    [\"State\"] = Result,\r\n     MessageDetails = Description"
version: 1.0.1
kind: Scheduled
customDetails:
  Date: Date
  DataSource: DataSource
  EventId: EventId
  MessageDetails: MessageDetails

Stages and Predicates

Stage 1: source

Veeam_GetFinishedConfigurationBackupSessions

Stage 2: where

| where Result == "Failed"

Stage 3: project

| project
    Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
    DataSource = Computer,
    EventId = instanceId,
    UserName = user,
    ["State"] = Result,
     MessageDetails = Description

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Resulteq
  • Failed
field:"Result" kind:eq value:"Failed"

Output Fields

These fields are emitted when the rule matches.

FieldSource
DataSourceproject
Dateproject
EventIdproject
MessageDetailsproject
Stateproject
UserNameproject

CTE - Blocked URL Attempted

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

Cybereinforce blocked a URL/domain access attempt on an enrolled device based on a configured policy pattern (non-threat-intel match).

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 1747715b-1a94-411a-a2b5-d8c40422767c
name: CTE - Blocked URL Attempted
description: |
  Cybereinforce blocked a URL/domain access attempt on an enrolled device based on a configured policy pattern (non-threat-intel match).
severity: Low
requiredDataConnectors:
  - connectorId: cybereinforce_cte
    dataTypes:
      - CybereinforceCTE_CL
queryFrequency: 5m
queryPeriod: 5m
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)
  | where RuleName != "Cybereinforce Threat Intelligence"
  | project TimeGenerated, TenantId, DeviceId, HostName, DeviceUser, DeviceOs, DeviceVersion, RuleId, RuleName, Pattern, Url, Action, IsIoc, ClientIp, ClientPort, UserAgent, Severity, Category, Source
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 =
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: where

| where RuleName != "Cybereinforce Threat Intelligence"

Stage 6: project

| project TimeGenerated, TenantId, DeviceId, HostName, DeviceUser, DeviceOs, DeviceVersion, RuleId, RuleName, Pattern, Url, Action, IsIoc, ClientIp, ClientPort, UserAgent, Severity, Category, Source

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Actionproject
Categoryproject
ClientIpproject
ClientPortproject
DeviceIdproject
DeviceOsproject
DeviceUserproject
DeviceVersionproject
HostNameproject
IsIocproject
Patternproject
RuleIdproject
RuleNameproject
Severityproject
Sourceproject
TenantIdproject
TimeGeneratedproject
Urlproject
UserAgentproject

GTI - High and Critical Priority Alerts

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

Triggers an incident whenever a GTI Relevance System Alert with priority level HIGH or CRITICAL is ingested. These alerts indicate threats that Google Threat Intelligence has assessed as requiring immediate attention based on relevance, severity, and potential business impact. Each unique Alert ID is grouped into a single incident.

MITRE ATT&CK Coverage

Rule Body

id: b2c3d4e5-f6a7-8901-bcde-f12345678902
name: GTI - High and Critical Priority Alerts
description: |
  Triggers an incident whenever a GTI Relevance System Alert with priority level HIGH or CRITICAL
  is ingested. These alerts indicate threats that Google Threat Intelligence has assessed as requiring
  immediate attention based on relevance, severity, and potential business impact.
  Each unique Alert ID is grouped into a single incident.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: GoogleThreatIntelligenceRelevanceSystemAlertsAPI
    dataTypes:
      - RelevanceSystemAlerts_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - Impact
  - CredentialAccess
  - Exfiltration
relevantTechniques:
  - T1566
  - T1078
  - T1552
  - T1486
  - T1567
query: |
  GTIRelevanceSystemAlerts
  | where PriorityLevel in ("HIGH", "CRITICAL")
  | extend
      DetailSeverity = coalesce(DataLeakSeverity, InsiderThreatSeverity, InitialAccessBrokerSeverity, "")
  | project
      TimeGenerated,
      AlertId,
      AlertName          = DisplayName,
      AlertState         = State,
      PriorityLevel,
      PriorityReasoning,
      SeverityLevel,
      SeverityConfidence,
      DetailType,
      DetailSeverity,
      RelevanceLevel,
      FindingCount,
      AiSummary,
      AuditCreateTime,
      AuditUpdateTime
alertDetailsOverride:
  alertDisplayNameFormat: 'GTI High/Critical Priority Alert: {{AlertName}}'
  alertDescriptionFormat: |
    Priority: {{PriorityLevel}}

    Priority Reasoning: {{PriorityReasoning}}

    Summary: {{AiSummary}}
customDetails:
  AlertId: AlertId
  AlertState: AlertState
  PriorityLevel: PriorityLevel
  SeverityLevel: SeverityLevel
  DetailType: DetailType
  FindingCount: FindingCount
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: P5D
    matchingMethod: Selected
    groupByCustomDetails:
      - AlertId
eventGroupingSettings:
  aggregationKind: AlertPerResult
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

GTIRelevanceSystemAlerts

Stage 2: where

| where PriorityLevel in ("HIGH", "CRITICAL")

Stage 3: extend

| extend
    DetailSeverity = coalesce(DataLeakSeverity, InsiderThreatSeverity, InitialAccessBrokerSeverity, "")

Stage 4: project

| project
    TimeGenerated,
    AlertId,
    AlertName          = DisplayName,
    AlertState         = State,
    PriorityLevel,
    PriorityReasoning,
    SeverityLevel,
    SeverityConfidence,
    DetailType,
    DetailSeverity,
    RelevanceLevel,
    FindingCount,
    AiSummary,
    AuditCreateTime,
    AuditUpdateTime

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
PriorityLevelin
  • CRITICAL
  • HIGH
field:"PriorityLevel" kind:in

Output Fields

These fields are emitted when the rule matches.

FieldSource
AiSummaryproject
AlertIdproject
AlertNameproject
AlertStateproject
AuditCreateTimeproject
AuditUpdateTimeproject
DetailSeverityproject
DetailTypeproject
FindingCountproject
PriorityLevelproject
PriorityReasoningproject
RelevanceLevelproject
SeverityConfidenceproject
SeverityLevelproject
TimeGeneratedproject

McAfee ePO - Attempt uninstall McAfee agent

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

Detects attempts uninstalling McAfee agent on host.

MITRE ATT&CK Coverage

TacticTechniques
Stealth
Defense Impairment

Telemetry Coverage

Rule Body

id: 2eff5809-bf84-48e0-8288-768689672c37
name: McAfee ePO - Attempt uninstall McAfee agent
description: |
  'Detects attempts uninstalling McAfee agent on host.'
severity: Medium
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1562
  - T1070
query: |
  McAfeeEPOEvent
  | where EventId == '2413'
  | extend IPCustomEntity = DvcIpAddr, HostCustomEntity = DvcHostname
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: HostCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

McAfeeEPOEvent

Stage 2: where

| where EventId == '2413'

Stage 3: extend

| extend IPCustomEntity = DvcIpAddr, HostCustomEntity = DvcHostname

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
HostCustomEntityextend
IPCustomEntityextend

Privileged Machines Exposed to the Internet

#

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
high
Time window
30m
Source
github.com/Azure/Azure-Sentinel

These are AWS Ec2 machines that are exposed to the internet. You can further filter by tags so that you can, for example, find exposed machines that are also "privileged".

MITRE ATT&CK Coverage

TacticTechniques
Discovery

Rule Body

id: 72891de4-da70-44e4-9984-35fcea98d000
name: Privileged Machines Exposed to the Internet
kind: Scheduled
description: These are AWS Ec2 machines that are exposed to the internet. You can further filter by tags so that you can, for example, find exposed machines that are also "privileged".
severity: High
status: Available
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
  - Impact
relevantTechniques:
  - T1580
query: |-
  Authomize_v2_CL
  | where ingestion_time() >= ago(30m)
  | extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s
  | where Policy has "Privileged Machines Exposed to the Internet"
  | project  EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: URL
requiredDataConnectors:
- connectorId: Authomize
  dataTypes: [ "Authomize_v2_CL" ]
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 5h
    matchingMethod: AnyAlert
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
eventGroupingSettings:
  aggregationKind: SingleAlert
alertDetailsOverride:
  alertnameFormat: Alert from Authomize - Privileged Machines Exposed to the Internet
  alertDescriptionFormat: Privileged Machines Exposed to the Internet. These are AWS Ec2 machines that are exposed to the internet. You can further filter by tags so that you can, for example, find exposed machines that are also "privileged".
  alertSeverity: Severity
  alertTactics: Tactics
  alertDynamicProperties:
  - alertProperty: AlertLink
    value: URL
customDetails:
  AuthomizeEventID: EventID
  EventName: Policy
  EventDescription: Description
  EventRecommendation: Recommendation
  ReferencedURL: URL
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.2

Stages and Predicates

Stage 1: source

Authomize_v2_CL

Stage 2: where

| where ingestion_time() >= ago(30m)

Stage 3: extend

| extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s

Stage 4: where

| where Policy has "Privileged Machines Exposed to the Internet"

Stage 5: project

| project  EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Policymatch
  • Privileged Machines Exposed to the Internet transforms: term
field:"policy_name_s" kind:match value:"Privileged Machines Exposed to the Internet"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Categoryproject
Descriptionproject
EventIDproject
Policyproject
Recommendationproject
Severityproject
Tacticsproject
URLproject

PROD (TM014.1) - GPO - Linked, Unlinked, or Enforced at the AD Site Level

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

Monitoring AD Site GPOs being Linked, Unlinked, or Enforced.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: 38f2f6f4-2869-401c-8341-e3a5c11018a5
name: 'PROD (TM014.1) - GPO - Linked, Unlinked, or Enforced at the AD Site Level'
description: |
  Monitoring AD Site GPOs being Linked, Unlinked, or Enforced.
severity: High
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1484.001
query: |
  SecurityEvent 
  | where EventID == 5136 
      and EventData matches regex @"(?i)CN=Sites"
      and Activity has "modified" 
      and EventData has '"ObjectClass">site'
      and EventData has '<Data Name="AttributeLDAPDisplayName">gPLink</Data>' 
      and (EventData has ';0]' 
          or EventData has ';1]' 
          or EventData has ';2]')
      and EventData has '"OperationType">%%14674'
  | extend ObjectName = extract("<Data Name=\"ObjectDN\">((?i)CN=[^,]+(?:,(?i)CN=[^,]+)*),DC=", 1, EventData)
  | extend AttributeLDAP = extract("<Data Name=\"AttributeLDAPDisplayName\">([^<]+)</Data>", 1, EventData)
  | extend AttributeValue = extract("\\[([^\\]]+)\\]", 1, EventData)
  | extend LastThreeChars = substring(AttributeValue, strlen(AttributeValue) - 2, 3)
  | extend AttributeValueDescription = case(
      LastThreeChars == ";0", "Linked",
      LastThreeChars == ";1", "Unlinked",
      LastThreeChars == ";2", "Enforced",
      "Unknown")
  | extend ObjectClass = extract("<Data Name=\"ObjectClass\">([^<]+)</Data>", 1, EventData)
  | extend Domain = extract("([^.]+\\.[^.]+)$", 1, Computer)
  | project TimeGenerated
     , ObjectName
     , ObjectClass
     , ActivityType = AttributeValueDescription
     , AttributeLDAP
     , Account
     , Domain
     , Computer
     , Channel
     , EventID
     , EventData
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: ObjectName
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: Computer
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Account
customDetails:
  ObjectName: ObjectName
  ObjectClass: ObjectClass
  ActivityType: ActivityType
  AttributeLDAP: AttributeLDAP
  Account: Account
  Domain: Domain
  Computer: Computer
  Channel: Channel
  EventID: EventID
  EventData: EventData
eventGroupingSettings:
  aggregationKind: AlertPerResult
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
alertDetailsOverride:
  alertDisplayNameFormat: '(TM014.1) A GPO was {{ActivityType}} at the Active Directory Site Level'
  alertDescriptionFormat: 'Site Level GPOs can overwrite Tier Model GPOs.'
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

SecurityEvent

Stage 2: where

| where EventID == 5136 
    and EventData matches regex @"(?i)CN=Sites"
    and Activity has "modified" 
    and EventData has '"ObjectClass">site'
    and EventData has '<Data Name="AttributeLDAPDisplayName">gPLink</Data>' 
    and (EventData has ';0]' 
        or EventData has ';1]' 
        or EventData has ';2]')
    and EventData has '"OperationType">%%14674'

Stage 3: extend (7 consecutive steps)

| extend ObjectName = extract("<Data Name=\"ObjectDN\">((?i)CN=[^,]+(?:,(?i)CN=[^,]+)*),DC=", 1, EventData)
| extend AttributeLDAP = extract("<Data Name=\"AttributeLDAPDisplayName\">([^<]+)</Data>", 1, EventData)
| extend AttributeValue = extract("\\[([^\\]]+)\\]", 1, EventData)
| extend LastThreeChars = substring(AttributeValue, strlen(AttributeValue) - 2, 3)
| extend AttributeValueDescription = case(
    LastThreeChars == ";0", "Linked",
    LastThreeChars == ";1", "Unlinked",
    LastThreeChars == ";2", "Enforced",
    "Unknown")
| extend ObjectClass = extract("<Data Name=\"ObjectClass\">([^<]+)</Data>", 1, EventData)
| extend Domain = extract("([^.]+\\.[^.]+)$", 1, Computer)

Stage 4: project

| project TimeGenerated
   , ObjectName
   , ObjectClass
   , ActivityType = AttributeValueDescription
   , AttributeLDAP
   , Account
   , Domain
   , Computer
   , Channel
   , EventID
   , EventData

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Activitymatch
  • modified transforms: term corpus 10 (kusto 10)
field:"Activity" kind:match value:"modified"
EventDatamatch
  • "ObjectClass">site transforms: term
  • "OperationType">%%14674 transforms: term corpus 7 (kusto 7)
  • ;0] transforms: term corpus 4 (kusto 4)
  • ;1] transforms: term corpus 4 (kusto 4)
  • ;2] transforms: term corpus 4 (kusto 4)
  • <Data Name="AttributeLDAPDisplayName">gPLink</Data> transforms: term corpus 4 (kusto 4)
field:"EventData" kind:match
EventDataregex_match
  • (?i)CN=Sites
field:"EventData" kind:regex_match value:"(?i)CN=Sites"
EventIDeq
  • 5136 corpus 20 (kusto 18, elastic 2)
field:"EventID" kind:eq value:"5136"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Accountproject
ActivityTypeproject
AttributeLDAPproject
Channelproject
Computerproject
Domainproject
EventDataproject
EventIDproject
ObjectClassproject
ObjectNameproject
TimeGeneratedproject

SAP BTP - Build Work Zone unauthorized access and role tampering

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

Identifies unauthorized OData access attempts and mass role/user deletions in SAP Build Work Zone Standard Edition. These events may indicate an attacker accessing restricted resources or removing access controls to cover their tracks.

MITRE ATT&CK Coverage

Rule Body

id: 8e5f3a2c-9d1b-4c6e-a7f8-3b2d1e0c9a5f
kind: Scheduled
name: SAP BTP - Build Work Zone unauthorized access and role tampering
description: |
  Identifies unauthorized OData access attempts and mass role/user deletions in SAP Build Work Zone 
  Standard Edition. These events may indicate an attacker accessing restricted resources or 
  removing access controls to cover their tracks.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: SAPBTPAuditEvents
    dataTypes:
      - SAPBTPAuditLog_CL
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - Persistence
  - DefenseEvasion
  - Impact
relevantTechniques:
  - T1078
  - T1531
  - T1070
query: |
  SAPBTPAuditLog_CL
  | extend LogMessage = coalesce(tostring(Message.message), tostring(Message.data), tostring(Message))
  | where LogMessage has_any ("Unauthorized access to the oData service", 
                              "All roles for providerId", 
                              "All users for providerId",
                              "were deleted successfully",
                              "were removed successfully")
  | extend EventCategory = case(
      LogMessage contains "Unauthorized access", "Unauthorized Access",
      LogMessage contains "All roles" or LogMessage contains "All users", "Mass Deletion",
      "Role Tampering"
  )
  | extend MessageText = case(
      LogMessage contains "Unauthorized access to the oData service", "Unauthorized OData service access attempt",
      LogMessage contains "All roles for providerId" and LogMessage contains "deleted", "Mass role deletion detected",
      LogMessage contains "All users for providerId" and LogMessage contains "deleted", "Mass user deletion detected",
      LogMessage contains "assignments were removed", "User/role assignments removed",
      "Suspicious access control modification"
  )
  | extend ProviderId = extract(@"providerId\s+(\S+)", 1, LogMessage)
  | project
      TimeGenerated,
      UserName,
      MessageText,
      EventCategory,
      ProviderId,
      Tenant,
      CloudApp = "SAP Build Work Zone Standard Edition"
  | extend AccountName = split(UserName, "@")[0], UPNSuffix = split(UserName, "@")[1]
eventGroupingSettings:
  aggregationKind: AlertPerResult
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: CloudApp
alertDetailsOverride:
  alertDisplayNameFormat: 'SAP Build Work Zone: {{MessageText}}'
  alertDescriptionFormat: |
    {{MessageText}} by {{UserName}} in tenant {{Tenant}}.
    
    This could indicate unauthorized access attempts or malicious removal of access controls.
customDetails:
  EventCategory: EventCategory
  ProviderId: ProviderId
version: 1.0.1

Stages and Predicates

Stage 1: source

SAPBTPAuditLog_CL

Stage 2: extend

| extend LogMessage = coalesce(tostring(Message.message), tostring(Message.data), tostring(Message))

Stage 3: where

| where LogMessage has_any ("Unauthorized access to the oData service", 
                            "All roles for providerId", 
                            "All users for providerId",
                            "were deleted successfully",
                            "were removed successfully")

Stage 4: extend (3 consecutive steps)

| extend EventCategory = case(
    LogMessage contains "Unauthorized access", "Unauthorized Access",
    LogMessage contains "All roles" or LogMessage contains "All users", "Mass Deletion",
    "Role Tampering"
)
| extend MessageText = case(
    LogMessage contains "Unauthorized access to the oData service", "Unauthorized OData service access attempt",
    LogMessage contains "All roles for providerId" and LogMessage contains "deleted", "Mass role deletion detected",
    LogMessage contains "All users for providerId" and LogMessage contains "deleted", "Mass user deletion detected",
    LogMessage contains "assignments were removed", "User/role assignments removed",
    "Suspicious access control modification"
)
| extend ProviderId = extract(@"providerId\s+(\S+)", 1, LogMessage)
EventCategory =
ifLogMessage contains "Unauthorized access""Unauthorized Access"
elif(LogMessage contains "All roles" or LogMessage contains "All users")"Mass Deletion"
else"Role Tampering"

Stage 5: project

| project
    TimeGenerated,
    UserName,
    MessageText,
    EventCategory,
    ProviderId,
    Tenant,
    CloudApp = "SAP Build Work Zone Standard Edition"

Stage 6: extend

| extend AccountName = split(UserName, "@")[0], UPNSuffix = split(UserName, "@")[1]

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
LogMessagematch
  • All roles for providerId transforms: term
  • All users for providerId transforms: term
  • Unauthorized access to the oData service transforms: term
  • were deleted successfully transforms: term
  • were removed successfully transforms: term
field:"LogMessage" kind:match

Output Fields

These fields are emitted when the rule matches.

FieldSource
CloudAppproject
EventCategoryproject
MessageTextproject
ProviderIdproject
Tenantproject
TimeGeneratedproject
UserNameproject
AccountNameextend
UPNSuffixextend

Squid proxy events for ToR proxies

#
Severity
low
Time window
1d
Source
github.com/Azure/Azure-Sentinel

'Check for Squid proxy events associated with common ToR proxies. This query presumes the default squid log format is being used. http://www.squid-cache.org/Doc/config/access_log/'

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: 90d3f6ec-80fb-48e0-9937-2c70c9df9bad
name: Squid proxy events for ToR proxies
description: |
  'Check for Squid proxy events associated with common ToR proxies. This query presumes the default squid log format is being used.
  http://www.squid-cache.org/Doc/config/access_log/'
severity: Low
requiredDataConnectors:
  - connectorId: Syslog
    dataTypes:
      - Syslog
  - connectorId: SyslogAma
    dataTypes:
      - Syslog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1090
  - T1008
query: |
  let DomainList = dynamic(["tor2web.org", "tor2web.com", "torlink.co", "onion.to", "onion.ink", "onion.cab", "onion.nu", "onion.link",
  "onion.it", "onion.city", "onion.direct", "onion.top", "onion.casa", "onion.plus", "onion.rip", "onion.dog", "tor2web.fi",
  "tor2web.blutmagie.de", "onion.sh", "onion.lu", "onion.pet", "t2w.pw", "tor2web.ae.org", "tor2web.io", "tor2web.xyz", "onion.lt",
  "s1.tor-gateways.de", "s2.tor-gateways.de", "s3.tor-gateways.de", "s4.tor-gateways.de", "s5.tor-gateways.de", "hiddenservice.net"]);
  Syslog
  | where ProcessName contains "squid"
  | extend URL = extract("(([A-Z]+ [a-z]{4,5}:\\/\\/)|[A-Z]+ )([^ :]*)",3,SyslogMessage),
          SourceIP = extract("([0-9]+ )(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3}))",2,SyslogMessage),
          Status = extract("(TCP_(([A-Z]+)(_[A-Z]+)*)|UDP_(([A-Z]+)(_[A-Z]+)*))",1,SyslogMessage),
          HTTP_Status_Code = extract("(TCP_(([A-Z]+)(_[A-Z]+)*)|UDP_(([A-Z]+)(_[A-Z]+)*))/([0-9]{3})",8,SyslogMessage),
          User = extract("(CONNECT |GET )([^ ]* )([^ ]+)",3,SyslogMessage),
          RemotePort = extract("(CONNECT |GET )([^ ]*)(:)([0-9]*)",4,SyslogMessage),
          Domain = extract("(([A-Z]+ [a-z]{4,5}:\\/\\/)|[A-Z]+ )([^ :\\/]*)",3,SyslogMessage),
          Bytes = toint(extract("([A-Z]+\\/[0-9]{3} )([0-9]+)",2,SyslogMessage)),
          contentType = extract("([a-z/]+$)",1,SyslogMessage)
  | extend TLD = extract("\\.[a-z]*$",0,Domain)
  | where HTTP_Status_Code == "200"
  | where Domain contains "."
  | where Domain has_any (DomainList)
  | extend AccountName = tostring(split(User, "@")[0]), AccountUPNSuffix = tostring(split(User, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: User
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIP
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: URL
version: 1.0.3
kind: Scheduled

Stages and Predicates

Let Binding: DomainList used in Stage 7

let DomainList = dynamic(["tor2web.org", "tor2web.com", "torlink.co", "onion.to", "onion.ink", "onion.cab", "onion.nu", "onion.link",
"onion.it", "onion.city", "onion.direct", "onion.top", "onion.casa", "onion.plus", "onion.rip", "onion.dog", "tor2web.fi",
"tor2web.blutmagie.de", "onion.sh", "onion.lu", "onion.pet", "t2w.pw", "tor2web.ae.org", "tor2web.io", "tor2web.xyz", "onion.lt",
"s1.tor-gateways.de", "s2.tor-gateways.de", "s3.tor-gateways.de", "s4.tor-gateways.de", "s5.tor-gateways.de", "hiddenservice.net"]);

Stage 1: source

Syslog

Stage 2: where

| where ProcessName contains "squid"

Stage 3: extend

| extend URL = extract("(([A-Z]+ [a-z]{4,5}:\\/\\/)|[A-Z]+ )([^ :]*)",3,SyslogMessage),
        SourceIP = extract("([0-9]+ )(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3}))",2,SyslogMessage),
        Status = extract("(TCP_(([A-Z]+)(_[A-Z]+)*)|UDP_(([A-Z]+)(_[A-Z]+)*))",1,SyslogMessage),
        HTTP_Status_Code = extract("(TCP_(([A-Z]+)(_[A-Z]+)*)|UDP_(([A-Z]+)(_[A-Z]+)*))/([0-9]{3})",8,SyslogMessage),
        User = extract("(CONNECT |GET )([^ ]* )([^ ]+)",3,SyslogMessage),
        RemotePort = extract("(CONNECT |GET )([^ ]*)(:)([0-9]*)",4,SyslogMessage),
        Domain = extract("(([A-Z]+ [a-z]{4,5}:\\/\\/)|[A-Z]+ )([^ :\\/]*)",3,SyslogMessage),
        Bytes = toint(extract("([A-Z]+\\/[0-9]{3} )([0-9]+)",2,SyslogMessage)),
        contentType = extract("([a-z/]+$)",1,SyslogMessage)

Stage 4: extend

| extend TLD = extract("\\.[a-z]*$",0,Domain)

Stage 5: where

| where HTTP_Status_Code == "200"

Stage 6: where

| where Domain contains "."

Stage 7: where

| where Domain has_any (DomainList)

Stage 8: extend

| extend AccountName = tostring(split(User, "@")[0]), AccountUPNSuffix = tostring(split(User, "@")[1])

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Domaincontains
  • .
field:"Domain" kind:contains value:"."
Domainmatch
  • hiddenservice.net transforms: term
  • onion.cab transforms: term
  • onion.casa transforms: term
  • onion.city transforms: term
  • onion.direct transforms: term
  • onion.dog transforms: term
  • onion.ink transforms: term
  • onion.it transforms: term
  • onion.link transforms: term
  • onion.lt transforms: term
  • onion.lu transforms: term
  • onion.nu transforms: term
  • onion.pet transforms: term
  • onion.plus transforms: term
  • onion.rip transforms: term
  • onion.sh transforms: term
  • onion.to transforms: term
  • onion.top transforms: term
  • s1.tor-gateways.de transforms: term
  • s2.tor-gateways.de transforms: term
  • s3.tor-gateways.de transforms: term
  • s4.tor-gateways.de transforms: term
  • s5.tor-gateways.de transforms: term
  • t2w.pw transforms: term
  • tor2web.ae.org transforms: term
  • tor2web.blutmagie.de transforms: term
  • tor2web.com transforms: term
  • tor2web.fi transforms: term
  • tor2web.io transforms: term
  • tor2web.org transforms: term
  • tor2web.xyz transforms: term
  • torlink.co transforms: term
field:"Domain" kind:match
HTTP_Status_Codeeq
  • 200
field:"HTTP_Status_Code" kind:eq value:"200"
ProcessNamecontains
  • squid
field:"process_name" kind:contains value:"squid"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Bytesextend
Domainextend
HTTP_Status_Codeextend
RemotePortextend
SourceIPextend
Statusextend
URLextend
Userextend
contentTypeextend
TLDextend
AccountNameextend
AccountUPNSuffixextend

Suspicious granting of permissions to an account

#
Status
available
Severity
medium
Time window
1d
Group by
$left.PrincipalId, $left.RoleAssignedID, $right.AccountObjectId, $right.ID, Caller, CallerIpAddress, PrincipalId, RoleAssignedID
Source
github.com/Azure/Azure-Sentinel

Identifies IPs from which users grant access to other users on Azure resources and alerts when a previously unseen source IP address is used.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: b2c15736-b9eb-4dae-8b02-3016b6a45a32
name: Suspicious granting of permissions to an account
description: |
  'Identifies IPs from which users grant access to other users on Azure resources and alerts when a previously unseen source IP address is used.'
severity: Medium
status: Available 
requiredDataConnectors:
  - connectorId: AzureActivity
    dataTypes:
      - AzureActivity
  - connectorId: BehaviorAnalytics
    dataTypes:
      - IdentityInfo
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
  - PrivilegeEscalation
relevantTechniques:
  - T1098
  - T1548
query: |
  let starttime = 14d;
  let endtime = 1d;
  // The number of operations above which an IP address is considered an unusual source of role assignment operations
  let alertOperationThreshold = 5;
  let AzureBuiltInRole = externaldata(Role:string,RoleDescription:string,ID:string) [@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/AzureBuiltInRole.csv"] with (format="csv", ignoreFirstRecord=True);
  let createRoleAssignmentActivity = AzureActivity
  | where OperationNameValue =~ "microsoft.authorization/roleassignments/write";
  let RoleAssignedActivity = createRoleAssignmentActivity 
  | where TimeGenerated between (ago(starttime) .. ago(endtime))
  | summarize count() by CallerIpAddress, Caller, bin(TimeGenerated, 1d)
  | where count_ >= alertOperationThreshold
  // Returns all the records from the right side that don't have matches from the left.
  | join kind = rightanti ( 
  createRoleAssignmentActivity
  | where TimeGenerated > ago(endtime)
  | extend parsed_property = tostring(parse_json(Properties).requestbody)
  | extend PrincipalId = case(parsed_property has_cs 'PrincipalId',parse_json(parsed_property).Properties.PrincipalId, parsed_property has_cs 'principalId',parse_json(parsed_property).properties.principalId,"")
  | extend PrincipalType = case(parsed_property has_cs 'PrincipalType',parse_json(parsed_property).Properties.PrincipalType, parsed_property has_cs 'principalType',parse_json(parsed_property).properties.principalType, "")
  | extend Scope = case(parsed_property has_cs 'Scope',parse_json(parsed_property).Properties.Scope, parsed_property has_cs 'scope',parse_json(parsed_property).properties.scope,"")
  | extend RoleAddedDetails = case(parsed_property has_cs 'RoleDefinitionId',parse_json(parsed_property).Properties.RoleDefinitionId,parsed_property has_cs 'roleDefinitionId',parse_json(parsed_property).properties.roleDefinitionId,"")
  | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), ActivityTimeStamp = make_set(TimeGenerated), ActivityStatusValue = make_set(ActivityStatusValue), CorrelationId = make_set(CorrelationId), ActivityCountByCallerIPAddress = count()  
  by ResourceId, CallerIpAddress, Caller, OperationNameValue, Resource, ResourceGroup, PrincipalId, PrincipalType, Scope, RoleAddedDetails
  ) on CallerIpAddress, Caller
  | extend timestamp = StartTimeUtc, AccountCustomEntity = Caller, IPCustomEntity = CallerIpAddress;
  let RoleAssignedActivitywithRoleDetails = RoleAssignedActivity
  | extend RoleAssignedID = tostring(split(RoleAddedDetails, "/")[-1])
  // Returns all matching records from left and right sides.
  | join kind = inner (AzureBuiltInRole 
  ) on $left.RoleAssignedID == $right.ID;
  let CallerIPCountSummary = RoleAssignedActivitywithRoleDetails | summarize AssignmentCountbyCaller = count() by Caller, CallerIpAddress;
  let RoleAssignedActivityWithCount = RoleAssignedActivitywithRoleDetails | join kind = inner (CallerIPCountSummary | project Caller, AssignmentCountbyCaller, CallerIpAddress) on Caller, CallerIpAddress;
  RoleAssignedActivityWithCount
  | summarize arg_max(StartTimeUtc, *) by PrincipalId, RoleAssignedID
  // 	Returns all the records from the left side and only matching records from the right side.
  | join kind = leftouter( IdentityInfo
  | summarize arg_max(TimeGenerated, *) by AccountObjectId
  ) on $left.PrincipalId == $right.AccountObjectId
  // Check if assignment count is greater than the threshold.
  | where AssignmentCountbyCaller >= alertOperationThreshold
  | project ActivityTimeStamp, OperationNameValue, Caller, CallerIpAddress, PrincipalId, RoleAssignedID, RoleAddedDetails, Role, RoleDescription, AccountUPN, AccountCreationTime, GroupMembership, UserType, ActivityStatusValue, ResourceGroup, PrincipalType, Scope, CorrelationId, timestamp, AccountCustomEntity, IPCustomEntity, AssignmentCountbyCaller
  | extend Name = tostring(split(Caller,'@',0)[0]), UPNSuffix = tostring(split(Caller,'@',1)[0])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Caller
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: CallerIpAddress
version: 2.0.2
kind: Scheduled

Stages and Predicates

Parameters

let starttime = 14d;
let endtime = 1d;
let alertOperationThreshold = 5;

let createRoleAssignmentActivity, let RoleAssignedActivity, let RoleAssignedActivitywithRoleDetails and let RoleAssignedActivityWithCount are inlined into the numbered stages below.

Let Binding: AzureBuiltInRole used in Stage 9

let AzureBuiltInRole = externaldata(Role:string,RoleDescription:string,ID:string) [@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/AzureBuiltInRole.csv"] with (format="csv", ignoreFirstRecord=True);

Let Binding: CallerIPCountSummary used in Stage 10

let CallerIPCountSummary = RoleAssignedActivitywithRoleDetails | summarize AssignmentCountbyCaller = count() by Caller, CallerIpAddress;

Stages 1 to 10 define let RoleAssignedActivityWithCount as the rule's main pipeline source. Stages 11 to 15 use it.

Stage 1: source

AzureActivity

Stage 2: where

where OperationNameValue =~ "microsoft.authorization/roleassignments/write"

Stage 3: where

where TimeGenerated between (ago(1209600s) .. ago(86400s))

Stage 4: summarize

summarize by CallerIpAddress, Caller

Stage 5: where

where count_ >= 5

Stage 6: join

| join kind = rightanti ( 
createRoleAssignmentActivity
| where TimeGenerated > ago(endtime)
| extend parsed_property = tostring(parse_json(Properties).requestbody)
| extend PrincipalId = case(parsed_property has_cs 'PrincipalId',parse_json(parsed_property).Properties.PrincipalId, parsed_property has_cs 'principalId',parse_json(parsed_property).properties.principalId,"")
| extend PrincipalType = case(parsed_property has_cs 'PrincipalType',parse_json(parsed_property).Properties.PrincipalType, parsed_property has_cs 'principalType',parse_json(parsed_property).properties.principalType, "")
| extend Scope = case(parsed_property has_cs 'Scope',parse_json(parsed_property).Properties.Scope, parsed_property has_cs 'scope',parse_json(parsed_property).properties.scope,"")
| extend RoleAddedDetails = case(parsed_property has_cs 'RoleDefinitionId',parse_json(parsed_property).Properties.RoleDefinitionId,parsed_property has_cs 'roleDefinitionId',parse_json(parsed_property).properties.roleDefinitionId,"")
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), ActivityTimeStamp = make_set(TimeGenerated), ActivityStatusValue = make_set(ActivityStatusValue), CorrelationId = make_set(CorrelationId), ActivityCountByCallerIPAddress = count()  
by ResourceId, CallerIpAddress, Caller, OperationNameValue, Resource, ResourceGroup, PrincipalId, PrincipalType, Scope, RoleAddedDetails
) on CallerIpAddress, Caller

Stage 7: extend

extend AccountCustomEntity, IPCustomEntity, timestamp

Stage 8: extend

extend RoleAssignedID

Stage 9: join

join kind=inner (AzureBuiltInRole) on $left.RoleAssignedID, $right.ID

Stage 10: join

join kind=inner (CallerIPCountSummary) on Caller, CallerIpAddress

Stage 11: summarize

summarize by PrincipalId, RoleAssignedID

Stage 12: join

| join kind = leftouter( IdentityInfo
| summarize arg_max(TimeGenerated, *) by AccountObjectId
) on $left.PrincipalId == $right.AccountObjectId

Stage 13: where

where AssignmentCountbyCaller >= 5

Stage 14: project

project AccountCreationTime, AccountCustomEntity, AccountUPN, ActivityStatusValue, ActivityTimeStamp, AssignmentCountbyCaller, Caller, CallerIpAddress, CorrelationId, GroupMembership, IPCustomEntity, OperationNameValue, PrincipalId, PrincipalType, ResourceGroup, Role, RoleAddedDetails, RoleAssignedID, RoleDescription, Scope, UserType, timestamp

Stage 15: extend

extend Name, UPNSuffix

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
AssignmentCountbyCallerge
  • 5 transforms: number
field:"AssignmentCountbyCaller" kind:ge value:"5"
OperationNameValueeq
  • microsoft.authorization/roleassignments/write
  • microsoft.authorization/roleassignments/write
field:"azure_ad::operation_name_value" kind:eq
count_ge
  • 5 transforms: number
field:"count_" kind:ge value:"5"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCreationTimeproject
AccountCustomEntityproject
AccountUPNproject
ActivityStatusValueproject
ActivityTimeStampproject
AssignmentCountbyCallerproject
Callerproject
CallerIpAddressproject
CorrelationIdproject
GroupMembershipproject
IPCustomEntityproject
OperationNameValueproject
PrincipalIdproject
PrincipalTypeproject
ResourceGroupproject
Roleproject
RoleAddedDetailsproject
RoleAssignedIDproject
RoleDescriptionproject
Scopeproject
UserTypeproject
timestampproject
Nameextend
UPNSuffixextend

TIE Indicators of Exposures

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

'Searches for triggered Indicators of Exposures.'

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Rule Body

id: f6ae2eb2-97c9-4e0f-ae73-7420ef80d99d
name: TIE Indicators of Exposures
description: |
  'Searches for triggered Indicators of Exposures.'
severity: Low
requiredDataConnectors:
  - connectorId: TenableIE
    dataTypes:
      - Tenable_IE_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.yaml parser into the workspace
  // Retrieve the parser here: https://aka.ms/sentinel-TenableApp-afad-parser
  // Then, create the Kusto Function with alias afad_parser
  let SeverityTable=datatable(Severity:string,Level:int) [
    "low", 1,
    "medium", 2,
    "high", 3,
    "critical", 4
    ];
    afad_parser
    | where MessageType == 0
    | 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
  ];

Stage 1: source

afad_parser

Stage 2: where

| where MessageType == 0

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.

FieldKindValuesSearch
MessageTypeeq
  • 0 corpus 15 (kusto 15)
field:"MessageType" kind:eq value:"0"

Output Fields

These fields are emitted when the rule matches.

FieldSource
DnsDomainextend
HostNameextend

Tomcat - Multiple server errors from single IP address

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

'Detects multiple server errors from one source in short timeframe'

MITRE ATT&CK Coverage

Rule Body

id: de9df79c-4872-11ec-81d3-0242ac130003
name: Tomcat - Multiple server errors from single IP address
description: |
  'Detects multiple server errors from one source in short timeframe'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: CustomLogsAma
    datatypes:
      - Tomcat_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
  - InitialAccess
relevantTechniques:
  - T1498
  - T1190
  - T1133
query: |
  let threshold = 100;
  TomcatEvent
  | where HttpStatusCode >= 500 and HttpStatusCode <= 599 
  | summarize MultipleServerErrors = count() by SrcIpAddr, bin(TimeGenerated, 5m)
  | where MultipleServerErrors > threshold
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 100;

Stage 1: source

TomcatEvent

Stage 2: where

| where HttpStatusCode >= 500 and HttpStatusCode <= 599

Stage 3: summarize

| summarize MultipleServerErrors = count() by SrcIpAddr, bin(TimeGenerated, 5m)

Stage 4: where

| where MultipleServerErrors > threshold

Stage 5: extend

| extend IPCustomEntity = SrcIpAddr

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
MultipleServerErrorssummarize
SrcIpAddrsummarize
IPCustomEntityextend

Back to all Kusto rules