Detection rules › Kusto

Kusto rules: 06

1Password - New service account integration created

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

This will alert when a new integration has been created. Once this analytics rule is triggered it will group all related future alerts for upto an hour when all related entities are the same. Ref: https://1password.com/ Ref: https://github.com/securehats/

MITRE ATT&CK Coverage

TacticTechniques
Persistence

Telemetry Coverage

PlatformRecord / event type
1Passwordsa-create: Create Integration

Rule Body

id: 26daed54-cea5-469c-9b6e-0d85a40dc463
name: 1Password - New service account integration created
version: 1.0.0
kind: Scheduled
description: |-
  This will alert when a new integration has been created. Once this analytics rule is triggered it will group all related future alerts for upto an hour when all related entities are the same.

  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 log_source == "auditevents"
  | where action == "create"
  | where object_type == "sa"
  | extend
      ActorUsername = actor_details.email
      , SrcIpAddr = session.ip
subTechniques:
- T1136.003
suppressionEnabled: false
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AllEntities
suppressionDuration: 5h
eventGroupingSettings:
  aggregationKind: SingleAlert
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: ActorUsername
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: SrcIpAddr

Stages and Predicates

Stage 1: source

OnePasswordEventLogs_CL

Stage 2: where

| where log_source == "auditevents"

Stage 3: where

| where action == "create"

Stage 4: where

| where object_type == "sa"

Stage 5: extend

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

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActorUsernameextend
SrcIpAddrextend

Alsid Indicators of Attack

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

'Searches for triggered Indicators of Attack'

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Rule Body

id: 3caa67ef-8ed3-4ab5-baf2-3850d3667f3d
name: Alsid Indicators of Attack
description: |
    'Searches for triggered Indicators of Attack'
severity: Low
requiredDataConnectors:
  - connectorId: AlsidForAD
    dataTypes:
      - AlsidForADLog_CL
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - CredentialAccess
relevantTechniques:
  - T1110
query: |
    let SeverityTable=datatable(Severity:string,Level:int) [
    "low", 1,
    "medium", 2,
    "high", 3,
    "critical", 4
    ];
    afad_parser
    | where MessageType == 2
    | 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 == 2

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
  • 2 corpus 21 (kusto 21)
field:"MessageType" kind:eq value:"2"

Output Fields

These fields are emitted when the rule matches.

FieldSource
DnsDomainextend
HostNameextend

AWSCloudTrail - ECR image scan findings high or critical

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

Identifies Amazon ECR image scan findings that report high or critical severity vulnerabilities. These findings indicate container images that should be reviewed and remediated before deployment or continued use.

MITRE ATT&CK Coverage

TacticTechniques
Discovery

Telemetry Coverage

Rule Body

id: f6928301-56da-4d2c-aabe-e1a552bc8892
name: AWSCloudTrail - ECR image scan findings high or critical
description: |
  Identifies Amazon ECR image scan findings that report high or critical severity vulnerabilities. These findings indicate container images that should be reviewed and remediated before deployment or continued use.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: AWS
    dataTypes:
      - AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
relevantTechniques:
  - T1083
query: |
    AWSCloudTrail
    | where EventName == "DescribeImageScanFindings" and isempty(ErrorCode) and isempty(ErrorMessage)
    | extend repoName = tostring(parse_json(ResponseElements).repositoryName)
    | extend imageId = tostring(parse_json(ResponseElements).imageId.imageDigest)
    | extend Critical = toint(parse_json(ResponseElements).imageScanFindings.findingSeverityCounts.CRITICAL)
    | extend High = toint(parse_json(ResponseElements).imageScanFindings.findingSeverityCounts.HIGH)
    | where Critical > 0 or High > 0
    | 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]), "")
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
      - identifier: CloudAppAccountId
        columnName: RecipientAccountId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIpAddress
customDetails:
  Repository: repoName
  ImageDigest: imageId
  CriticalFindings: Critical
  HighFindings: High
alertDetailsOverride:
  alertDisplayNameFormat: 'AWS ECR image scan findings with high or critical Vulnerabilities in {{repoName}}'
  alertDescriptionFormat: 'ECR image {{imageId}} in repository {{repoName}} returned critical and high findings.'
version: 1.0.3
kind: Scheduled

Stages and Predicates

Stage 1: source

AWSCloudTrail

Stage 2: where

| where EventName == "DescribeImageScanFindings" and isempty(ErrorCode) and isempty(ErrorMessage)

Stage 3: extend (4 consecutive steps)

| extend repoName = tostring(parse_json(ResponseElements).repositoryName)
| extend imageId = tostring(parse_json(ResponseElements).imageId.imageDigest)
| extend Critical = toint(parse_json(ResponseElements).imageScanFindings.findingSeverityCounts.CRITICAL)
| extend High = toint(parse_json(ResponseElements).imageScanFindings.findingSeverityCounts.HIGH)

Stage 4: where

| where Critical > 0 or High > 0

Stage 5: 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]), "")

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
repoNameextend
imageIdextend
Criticalextend
Highextend
UserIdentityArnextend
UserNameextend
AccountNameextend
AccountUPNSuffixextend

AWSCloudTrail - Successful API executed from a Tor exit node

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

Identifies successful AWS CloudTrail API activity originating from an IP address identified as a TOR exit node in the external TOR list hosted at https://firewalliplists.gypthecat.com/lists/kusto/kusto-tor-exit.csv.zip. The rule alerts only when CloudTrail indicates the request completed successfully with no ErrorCode and no ErrorMessage, and the source IP is present in the TOR exit node list.

MITRE ATT&CK Coverage

TacticTechniques
Execution

Rule Body

id: 0adab960-5565-4978-ba6d-044553e4acc4
name: AWSCloudTrail - Successful API executed from a Tor exit node
description: |
  Identifies successful AWS CloudTrail API activity originating from an IP address identified as a TOR exit node in
  the external TOR list hosted at https://firewalliplists.gypthecat.com/lists/kusto/kusto-tor-exit.csv.zip. The rule alerts
  only when CloudTrail indicates the request completed successfully with no ErrorCode and no ErrorMessage, and the source
  IP is present in the TOR exit node list.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: AWS
    dataTypes:
      - AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
relevantTechniques:
  - T1204
query: |
    let TorNodes = (
    externaldata (TorIP:string)
    [h@'https://firewalliplists.gypthecat.com/lists/kusto/kusto-tor-exit.csv.zip']
    with (ignoreFirstRecord=true));
    AWSCloudTrail
    | where SourceIpAddress in (TorNodes) and isempty(ErrorCode) and isempty(ErrorMessage)
    | 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]), "")
    | project TimeGenerated, SourceIpAddress, AccountName, AccountUPNSuffix, UserIdentityArn, UserIdentityUserName, UserIdentityPrincipalid, EventName, EventSource, AWSRegion, RecipientAccountId
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
      - identifier: CloudAppAccountId
        columnName: RecipientAccountId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIpAddress
customDetails:
  UserIdentityArn: UserIdentityArn
  EventName: EventName
  EventSource: EventSource
  AWSRegion: AWSRegion
alertDetailsOverride:
  alertDisplayNameFormat: 'Successful AWS API call from TOR exit node: {{AccountName}} from {{SourceIpAddress}}'
  alertDescriptionFormat: 'A successful AWS API call was observed from TOR exit node IP {{SourceIpAddress}} for account {{AccountName}}.'
version: 1.0.2
kind: Scheduled

Stages and Predicates

Let Binding: TorNodes used in Stage 2

let TorNodes = (
externaldata (TorIP:string)
[h@'https://firewalliplists.gypthecat.com/lists/kusto/kusto-tor-exit.csv.zip']
with (ignoreFirstRecord=true));

Stage 1: source

AWSCloudTrail

Stage 2: where

| where SourceIpAddress in (TorNodes) and isempty(ErrorCode) and isempty(ErrorMessage)

Stage 3: 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 4: project

| project TimeGenerated, SourceIpAddress, AccountName, AccountUPNSuffix, UserIdentityArn, UserIdentityUserName, UserIdentityPrincipalid, EventName, EventSource, AWSRegion, RecipientAccountId

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AWSRegionproject
AccountNameproject
AccountUPNSuffixproject
EventNameproject
EventSourceproject
RecipientAccountIdproject
SourceIpAddressproject
TimeGeneratedproject
UserIdentityArnproject
UserIdentityPrincipalidproject
UserIdentityUserNameproject

BloodHound Attack Path Finding - Large Default Groups With Add Self Privileges

#

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 - Large Default Groups With Add Self Privileges

Rule Body

id: 6a9a4ef0-e08b-40ad-ad40-c88b6ab915ae
name: BloodHound Attack Path Finding - Large Default Groups With Add Self Privileges
description: BloodHound Attack Path Finding - Large Default Groups With Add Self Privileges
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 == "LargeDefaultGroupsAddSelf"
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 == "LargeDefaultGroupsAddSelf"

Indicators

These rows show field, operator, and value matches.

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

Cisco Cloud Security - Connection to non-corporate private network

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

'IP addresses of broadband links that usually indicates users attempting to access their home network, for example for a remote session to a home computer.'

MITRE ATT&CK Coverage

TacticTechniques
Command & Control
Exfiltration

Rule Body

id: c9b6d281-b96b-4763-b728-9a04b9fe1246
name: Cisco Cloud Security - Connection to non-corporate private network
description: |
  'IP addresses of broadband links that usually indicates users attempting to access their home network, for example for a remote session to a home computer.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: CiscoUmbrellaDataConnector
    dataTypes:
      - Cisco_Umbrella_proxy_CL
queryFrequency: 10m
queryPeriod: 10m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
  - Exfiltration
relevantTechniques:
  - T1573
  - T1041
query: |
  Cisco_Umbrella
  | where EventType == 'proxylogs'
  | where DvcAction =~ 'Allowed'
  | where UrlCategory has_any ('Dynamic and Residential', 'Personal VPN')
  | project TimeGenerated, SrcIpAddr, DstIpAddr, Identities 
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
kind: Scheduled
version: 1.0.3

Stages and Predicates

Stage 1: source

Cisco_Umbrella

Stage 2: where

| where EventType == 'proxylogs'

Stage 3: where

| where DvcAction =~ 'Allowed'

Stage 4: where

| where UrlCategory has_any ('Dynamic and Residential', 'Personal VPN')

Stage 5: project

| project TimeGenerated, SrcIpAddr, DstIpAddr, Identities

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
DvcActioneq
  • Allowed
field:"DvcAction" kind:eq value:"Allowed"
EventTypeeq
  • proxylogs
field:"EventType" kind:eq value:"proxylogs"
UrlCategorymatch
  • Dynamic and Residential transforms: term
  • Personal VPN transforms: term
field:"UrlCategory" kind:match

Output Fields

These fields are emitted when the rule matches.

FieldSource
DstIpAddrproject
Identitiesproject
SrcIpAddrproject
TimeGeneratedproject

CYFIRMA - Brand Intelligence - Product/Solution High 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.

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

"This alert is raised when CYFIRMA detects a critical reputation score for an IP address linked to your infrastructure. The IP has been previously associated with hacking activity and web application attacks. Denied outbound traffic to a foreign country from a known Microsoft data center IP suggests potential misuse or compromise of cloud infrastructure."

MITRE ATT&CK Coverage

Rule Body

id: 3a9a81bc-2f41-4d68-9cd1-7788326c92b1
name: CYFIRMA - Brand Intelligence - Product/Solution High Rule
description: |
  "This alert is raised when CYFIRMA detects a critical reputation score for an IP address linked to your infrastructure. 
  The IP has been previously associated with hacking activity and web application attacks. 
  Denied outbound traffic to a foreign country from a known Microsoft data center IP suggests potential misuse or compromise of cloud infrastructure."
version: 1.0.1
kind: Scheduled
severity: High
requiredDataConnectors:
  - connectorId: CyfirmaBrandIntelligenceAlertsDC
    dataTypes:
      - CyfirmaBIProductSolutionAlerts_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - ResourceDevelopment
  - InitialAccess
relevantTechniques:
  - T1585.002
  - T1583.001
  - T1566
  - T1583
query: |
  // High severity - Product/Solution Impersonation
  let timeFrame = 5m;
  CyfirmaBIProductSolutionAlerts_CL
  | where severity == 'Critical' and TimeGenerated between (ago(timeFrame) .. now())
  | extend
      Description=description,
      FirstSeen=first_seen,
      LastSeen=last_seen,
      RiskScore=risk_score,
      AlertUID=alert_uid,
      UID=uid,
      AssetType=asset_type,
      AssetValue=asset_value,
      Impact=impact,
      Recommendation=recommendation,
      SourceSype=source_type,
      ProviderName='CYFIRMA',
      ProductName='DeCYFIR/DeTCT'
  | project
      TimeGenerated,
      Description,
      RiskScore,
      FirstSeen,
      LastSeen,
      AlertUID,
      UID,
      AssetType,
      AssetValue,
      Impact,
      Recommendation,
      SourceSype,
      ProductName,
      ProviderName
customDetails:
  AssetType: AssetType
  AssetValue: AssetValue
  Impact: Impact
  Recommendation: Recommendation
  SourceSype: SourceSype
  Description: Description
  RiskScore: RiskScore
  TimeGenerated: TimeGenerated
  FirstSeen: FirstSeen
  LastSeen: LastSeen
  AlertUID: AlertUID
  UID: UID
alertDetailsOverride:
  alertDisplayNameFormat: "CYFIRMA - Brand Impersonation Detected for Product/Solution - High Severity - {{AssetType}} : {{AssetValue}} "
  alertDescriptionFormat: "{{Description}} "
  alertDynamicProperties:
    - alertProperty: ProductName
      value: ProductName
    - alertProperty: ProviderName
      value: ProviderName
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

CyfirmaBIProductSolutionAlerts_CL

Stage 2: where

| where severity == 'Critical' and TimeGenerated between (ago(timeFrame) .. now())

Stage 3: extend

| extend
    Description=description,
    FirstSeen=first_seen,
    LastSeen=last_seen,
    RiskScore=risk_score,
    AlertUID=alert_uid,
    UID=uid,
    AssetType=asset_type,
    AssetValue=asset_value,
    Impact=impact,
    Recommendation=recommendation,
    SourceSype=source_type,
    ProviderName='CYFIRMA',
    ProductName='DeCYFIR/DeTCT'

Stage 4: project

| project
    TimeGenerated,
    Description,
    RiskScore,
    FirstSeen,
    LastSeen,
    AlertUID,
    UID,
    AssetType,
    AssetValue,
    Impact,
    Recommendation,
    SourceSype,
    ProductName,
    ProviderName

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AlertUIDproject
AssetTypeproject
AssetValueproject
Descriptionproject
FirstSeenproject
Impactproject
LastSeenproject
ProductNameproject
ProviderNameproject
Recommendationproject
RiskScoreproject
SourceSypeproject
TimeGeneratedproject
UIDproject

CYFIRMA - Public Accounts Leaks Detection 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.

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

"Detects exposed public-facing account credentials as identified in CYFIRMA's threat intelligence feeds. This rule monitors for credentials leaked through third-party breaches, dark web sources, or public repositories that could impact the organization's users or systems. It captures key details such as email, username, IP address, and associated devices. These accounts may not be directly managed by the enterprise but still pose a risk of lateral access, shadow IT, or third-party exposure."

MITRE ATT&CK Coverage

TacticTechniques
Initial Access
Credential Access
Discovery

Rule Body

id: 57602938-e95a-4fc3-9352-8d473ed256e1
name: CYFIRMA - Public Accounts Leaks Detection Rule
description: |
  "Detects exposed public-facing account credentials as identified in CYFIRMA's threat intelligence feeds.
  This rule monitors for credentials leaked through third-party breaches, dark web sources, or public repositories that could impact the organization's users or systems.
  It captures key details such as email, username, IP address, and associated devices. These accounts may not be directly managed by the enterprise but still pose a risk of lateral access, shadow IT, or third-party exposure."
version: 1.0.1
kind: Scheduled
severity: High
requiredDataConnectors:
  - connectorId: CyfirmaCompromisedAccountsDataConnector
    dataTypes:
      - CyfirmaCompromisedAccounts_CL
queryFrequency: 5m
queryPeriod: 5m
suppressionDuration: 6h
suppressionEnabled: true
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - CredentialAccess
  - InitialAccess
  - Discovery
relevantTechniques:
  - T1078
  - T1087
  - T1552
query: |
  // Public Accounts Leaks - Latest per UID
  let timeFrame = 5m;
  CyfirmaCompromisedAccounts_CL
  | where TimeGenerated between (ago(timeFrame) .. now())
      and Category has "Public Accounts Leaks"
  | extend 
      ProviderName = 'CYFIRMA',
      ProductName = 'DeCYFIR/DeTCT'
  | summarize arg_max(TimeGenerated, 
      email,
      user_name,
      password,
      url,
      ip,
      computer_name,
      operating_system,
      breach_date,
      first_seen,
      last_seen,
      impact,
      recommendations,
      description,
      source,
      ProductName,
      ProviderName
  ) by uid
  | sort by TimeGenerated desc
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: user_name
      - identifier: UPNSuffix
        columnName: email
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: computer_name
      - identifier: OSVersion
        columnName: operating_system
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ip
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: url
customDetails:
  UID: uid
  TimeGenerated: TimeGenerated
  BreachDate: breach_date
  FirstSeen: first_seen
  LastSeen: last_seen
  Impact: impact
  Recommendations: recommendations
  Source: source
  Description: description
alertDetailsOverride:
  alertDisplayNameFormat: "Public Leak - {{user_name}} - {{email}}"
  alertDescriptionFormat: "{{description}}"
  alertDynamicProperties:
    - alertProperty: ProductName
      value: ProductName
    - alertProperty: ProviderName
      value: ProviderName
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

CyfirmaCompromisedAccounts_CL

Stage 2: where

| where TimeGenerated between (ago(timeFrame) .. now())
    and Category has "Public Accounts Leaks"

Stage 3: extend

| extend 
    ProviderName = 'CYFIRMA',
    ProductName = 'DeCYFIR/DeTCT'

Stage 4: summarize

| summarize arg_max(TimeGenerated, 
    email,
    user_name,
    password,
    url,
    ip,
    computer_name,
    operating_system,
    breach_date,
    first_seen,
    last_seen,
    impact,
    recommendations,
    description,
    source,
    ProductName,
    ProviderName
) by uid

Stage 5: sort

| sort by TimeGenerated desc

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Categorymatch
  • Public Accounts Leaks transforms: term
field:"Category" kind:match value:"Public Accounts Leaks"

Output Fields

These fields are emitted when the rule matches.

FieldSource
uidsummarize

Dataverse - Removal of blocked file extensions

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

Identifies modifications to an environment's blocked file extensions and extracts the removed extension.

MITRE ATT&CK Coverage

TacticTechniques
Defense Evasion

Telemetry Coverage

Rule Body

id: 1b1061be-2595-4492-af6d-1c8a5fc9576d
kind: Scheduled
name: Dataverse - Removal of blocked file extensions
description: Identifies modifications to an environment's blocked file extensions
  and extracts the removed extension.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Dataverse
    dataTypes:
      - DataverseActivity
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1629
query: |
  let query_frequency = 1h;
  let default_attachments = split('ade;adp;app;asa;ashx;asmx;asp;bas;bat;cdx;cer;chm;class;cmd;com;config;cpl;crt;csh;dll;exe;fxp;hlp;hta;htr;htw;ida;idc;idq;inf;ins;isp;its;jar;js;jse;ksh;lnk;mad;maf;mag;mam;maq;mar;mas;mat;mau;mav;maw;mda;mdb;mde;mdt;mdw;mdz;msc;msh;msh1;msh1xml;msh2;msh2xml;mshxml;msi;msp;mst;ops;pcd;pif;prf;prg;printer;pst;reg;rem;scf;scr;sct;shb;shs;shtm;shtml;soap;stm;tmp;url;vb;vbe;vbs;vsmacros;vss;vst;vsw;ws;wsc;wsf;wsh', ";");
  DataverseActivity
  | where TimeGenerated >= ago(query_frequency)
  | where Message == "Update" and EntityName =~ 'organization'
  | mv-expand Fields
  | where Fields.Name == "blockedattachments"
  | extend
      UpdatedAttachments = split(tostring(Fields.Value), ";"),
      CloudAppId = int(32780),
      AccountName = tostring(split(UserId, '@')[0]),
      UPNSuffix = tostring(split(UserId, '@')[1])
  | extend RemovedAttachments = set_difference(default_attachments, UpdatedAttachments)
  | project
      TimeGenerated,
      UserId,
      ClientIp,
      InstanceUrl,
      RemovedAttachments,
      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 - Blocked file extension removed in {{InstanceUrl}} '
  alertDescriptionFormat: '{{UserId}} modified environment blocked extensions list.
    {{UserId}} removed the following extensions {{RemovedAttachments}}.'
version: 3.2.0

Stages and Predicates

Parameters

let query_frequency = 1h;

Let Binding: default_attachments used in Stage 7

let default_attachments = split('ade;adp;app;asa;ashx;asmx;asp;bas;bat;cdx;cer;chm;class;cmd;com;config;cpl;crt;csh;dll;exe;fxp;hlp;hta;htr;htw;ida;idc;idq;inf;ins;isp;its;jar;js;jse;ksh;lnk;mad;maf;mag;mam;maq;mar;mas;mat;mau;mav;maw;mda;mdb;mde;mdt;mdw;mdz;msc;msh;msh1;msh1xml;msh2;msh2xml;mshxml;msi;msp;mst;ops;pcd;pif;prf;prg;printer;pst;reg;rem;scf;scr;sct;shb;shs;shtm;shtml;soap;stm;tmp;url;vb;vbe;vbs;vsmacros;vss;vst;vsw;ws;wsc;wsf;wsh', ";");

Stage 1: source

DataverseActivity

Stage 2: where

| where TimeGenerated >= ago(query_frequency)

Stage 3: where

| where Message == "Update" and EntityName =~ 'organization'

Stage 4: mv-expand

| mv-expand Fields

Stage 5: where

| where Fields.Name == "blockedattachments"

Stage 6: extend

| extend
    UpdatedAttachments = split(tostring(Fields.Value), ";"),
    CloudAppId = int(32780),
    AccountName = tostring(split(UserId, '@')[0]),
    UPNSuffix = tostring(split(UserId, '@')[1])

Stage 7: extend

| extend RemovedAttachments = set_difference(default_attachments, UpdatedAttachments)

Stage 8: project

| project
    TimeGenerated,
    UserId,
    ClientIp,
    InstanceUrl,
    RemovedAttachments,
    CloudAppId,
    AccountName,
    UPNSuffix

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EntityNameeq
  • organization
field:"EntityName" kind:eq value:"organization"
Fields.Nameeq
  • blockedattachments transforms: kusto_dynamic_path
field:"Fields.Name" kind:eq value:"blockedattachments"
Messageeq
  • Update corpus 5 (kusto 5)
field:"Message" kind:eq value:"Update"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountNameproject
ClientIpproject
CloudAppIdproject
InstanceUrlproject
RemovedAttachmentsproject
TimeGeneratedproject
UPNSuffixproject
UserIdproject

Digital Guardian - Exfiltration to external domain

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

'Detects exfiltration to external domain.'

MITRE ATT&CK Coverage

Rule Body

id: a19885c8-1e44-47e3-81df-d1d109f5c92d
name: Digital Guardian - Exfiltration to external domain
description: |
  'Detects exfiltration to external domain.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Exfiltration
relevantTechniques:
  - T1048
query: |
  let corp_domain = dynamic(['example.com']);     //add all corporate domains to this list
  DigitalGuardianDLPEvent
  | where NetworkApplicationProtocol =~ 'SMTP'
  | where isnotempty(inspected_document)
  | extend s_domain = extract(@'@(.*)', 1, SrcUserName)
  | extend d_domain = extract(@'@(.*)', 1, DstUserName)
  | where s_domain in~ (corp_domain)
  | where d_domain !in (corp_domain)
  | extend AccountCustomEntity = SrcUserName
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let corp_domain = dynamic(['example.com']);

Stage 1: source

DigitalGuardianDLPEvent

Stage 2: where

| where NetworkApplicationProtocol =~ 'SMTP'

Stage 3: where

| where isnotempty(inspected_document)

Stage 4: extend

| extend s_domain = extract(@'@(.*)', 1, SrcUserName)

Stage 5: extend

| extend d_domain = extract(@'@(.*)', 1, DstUserName)

Stage 6: where

| where s_domain in~ (corp_domain)

Stage 7: where

| where d_domain !in (corp_domain)

Stage 8: extend

| extend AccountCustomEntity = SrcUserName

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
d_domaineqcorp_domainexcludes:d_domain field:"d_domain" value:"corp_domain"

Output Fields

These fields are emitted when the rule matches.

FieldSource
s_domainextend
d_domainextend
AccountCustomEntityextend

Exchange OAB Virtual Directory Attribute Containing Potential Webshell

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

This query uses Windows Event ID 5136 in order to detect potential webshell deployment by exploitation of CVE-2021-27065. This query looks for changes to the InternalHostName or ExternalHostName properties of Exchange OAB Virtual Directory objects in AD Directory Services where the new objects contain potential webshell objects.

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Telemetry Coverage

Rule Body

id: faf1a6ff-53b5-4f92-8c55-4b20e9957594
name: Exchange OAB Virtual Directory Attribute Containing Potential Webshell
description: |
  'This query uses Windows Event ID 5136 in order to detect potential webshell deployment by exploitation of CVE-2021-27065.
  This query looks for changes to the InternalHostName or ExternalHostName properties of Exchange OAB Virtual Directory objects in AD Directory Services where the new objects contain potential webshell objects.'
severity: High
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
query: |
  SecurityEvent
  // Look for specific Directory Service Changes and parse data
  | where EventID == 5136
  | extend EventData = parse_xml(EventData).EventData.Data
  | mv-expand bagexpansion = array EventData
  | evaluate bag_unpack(EventData)
  | extend Key = tostring(column_ifexists('@Name', "")), Value = column_ifexists('#text', "")
  | evaluate pivot(Key, any(Value),TimeGenerated, EventID, Computer, Account, AccountType, EventSourceName, Activity, SubjectAccount)
  // Where changes relate to Exchange OAB
  | extend ObjectClass = column_ifexists("ObjectClass", "")
  | where ObjectClass =~ "msExchOABVirtualDirectory"
  // Look for InternalHostName or ExternalHostName properties being changed
  | extend AttributeLDAPDisplayName = column_ifexists("AttributeLDAPDisplayName", "")
  | where AttributeLDAPDisplayName in~ ("msExchExternalHostName", "msExchInternalHostName")
  // Look for suspected webshell activity
  | extend AttributeValue = column_ifexists("AttributeValue", "")
  | where AttributeValue has "script"
  | project-rename LastSeen = TimeGenerated
  | extend ObjectDN = column_ifexists("ObjectDN", "")
  | project-reorder LastSeen, Computer, Account, ObjectDN, AttributeLDAPDisplayName, AttributeValue
  | extend timestamp = LastSeen
  | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
  | extend AccountName = tostring(split(Account, @'\')[1]), AccountNTDomain = tostring(split(Account, @'\')[0])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Account
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountNTDomain
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: HostNameDomain
version: 1.0.4
kind: Scheduled

Stages and Predicates

Stage 1: source

SecurityEvent

Stage 2: where

| where EventID == 5136

Stage 3: extend

| extend EventData = parse_xml(EventData).EventData.Data

Stage 4: mv-expand

| mv-expand bagexpansion = array EventData

Stage 5: evaluate

| evaluate bag_unpack(EventData)

Stage 6: extend

| extend Key = tostring(column_ifexists('@Name', "")), Value = column_ifexists('#text', "")

Stage 7: evaluate

| evaluate pivot(Key, any(Value),TimeGenerated, EventID, Computer, Account, AccountType, EventSourceName, Activity, SubjectAccount)

Stage 8: extend

| extend ObjectClass = column_ifexists("ObjectClass", "")

Stage 9: where

| where ObjectClass =~ "msExchOABVirtualDirectory"

Stage 10: extend

| extend AttributeLDAPDisplayName = column_ifexists("AttributeLDAPDisplayName", "")

Stage 11: where

| where AttributeLDAPDisplayName in~ ("msExchExternalHostName", "msExchInternalHostName")

Stage 12: extend

| extend AttributeValue = column_ifexists("AttributeValue", "")

Stage 13: where

| where AttributeValue has "script"

Stage 14: project-rename

| project-rename LastSeen = TimeGenerated

Stage 15: extend

| extend ObjectDN = column_ifexists("ObjectDN", "")

Stage 16: project-reorder

| project-reorder LastSeen, Computer, Account, ObjectDN, AttributeLDAPDisplayName, AttributeValue

Stage 17: extend (4 consecutive steps)

| extend timestamp = LastSeen
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
| extend AccountName = tostring(split(Account, @'\')[1]), AccountNTDomain = tostring(split(Account, @'\')[0])

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
EventDataextend
Keyextend
Valueextend
ObjectClassextend
AttributeLDAPDisplayNameextend
AttributeValueextend
LastSeenproject-rename
ObjectDNextend
timestampextend
DomainIndexextend
HostNameextend
HostNameDomainextend
AccountNTDomainextend
AccountNameextend

iboss - Malware Detected

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

Identifies web traffic where the iboss platform flagged malware. A populated MalwareDetected flag indicates the gateway detected a malicious file or payload associated with the request. Surfaces the destination, user, and other details to support triage.

MITRE ATT&CK Coverage

TacticTechniques
Execution

Rule Body

id: cad35734-b97a-4209-9269-b98c916379eb
name: iboss - Malware Detected
description: |
  Identifies web traffic where the iboss platform flagged malware. A populated MalwareDetected flag indicates the gateway detected a malicious file or payload associated with the request. Surfaces the destination, user, and other details to support triage.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: ibossAma
    dataTypes:
      - CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
relevantTechniques:
  - T1204
query: |
  ibossUrlEvent
  | where MalwareDetected == 1
  | extend FileHashAlgorithm = "SHA256"
  | project EventTime, SrcUsername, Url, Domain, SrcIpAddr, DstIpAddr, FileSHA256, FileHashAlgorithm,DvcAction, UrlCategory, HttpUserAgent
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: SrcUsername
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Url
  - entityType: FileHash
    fieldMappings:
      - identifier: Algorithm
        columnName: FileHashAlgorithm
      - identifier: Value
        columnName: FileSHA256
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

ibossUrlEvent

Stage 2: where

| where MalwareDetected == 1

Stage 3: extend

| extend FileHashAlgorithm = "SHA256"

Stage 4: project

| project EventTime, SrcUsername, Url, Domain, SrcIpAddr, DstIpAddr, FileSHA256, FileHashAlgorithm,DvcAction, UrlCategory, HttpUserAgent

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Domainproject
DstIpAddrproject
DvcActionproject
EventTimeproject
FileHashAlgorithmproject
FileSHA256project
HttpUserAgentproject
SrcIpAddrproject
SrcUsernameproject
Urlproject
UrlCategoryproject

Mass Cloud resource deletions Time Series Anomaly

#
Status
available
Severity
medium
Time window
14d
Group by
Caller, TimeGenerated
Source
github.com/Azure/Azure-Sentinel

'This query generates the baseline pattern of cloud resource deletions by an individual and generates an anomaly when any unusual spike is detected. These anomalies from unusual or privileged users could be an indication of a cloud infrastructure takedown by an adversary.'

MITRE ATT&CK Coverage

TacticTechniques
Impact

Rule Body

id: ed43bdb7-eaab-4ea4-be52-6951fcfa7e3b
name: Mass Cloud resource deletions Time Series Anomaly
description: |
  'This query generates the baseline pattern of cloud resource deletions by an individual and generates an anomaly when any unusual spike is detected. These anomalies from unusual or privileged users could be an indication of a cloud infrastructure takedown by an adversary.'
severity: Medium
status: Available 
requiredDataConnectors:
  - connectorId: AzureActivity
    dataTypes:
      - AzureActivity
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1485
tags:
  - DEV-0537
query: |
  let starttime = 14d;
  let endtime = 1d;
  let timeframe = 1d;
  let TotalEventsThreshold = 25;
  let TimeSeriesData = AzureActivity 
  | where TimeGenerated between (startofday(ago(starttime))..startofday(now())) 
  | where OperationNameValue endswith "delete" 
  | project TimeGenerated, Caller 
  | make-series Total = count() on TimeGenerated from startofday(ago(starttime)) to startofday(now()) step timeframe by Caller;
  TimeSeriesData 
  | extend (anomalies, score, baseline) = series_decompose_anomalies(Total, 3, -1, 'linefit') 
  | mv-expand Total to typeof(double), TimeGenerated to typeof(datetime), anomalies to typeof(double), score to typeof(double), baseline to typeof(long) 
  | where TimeGenerated >= startofday(ago(endtime)) 
  | where anomalies > 0 
  | project Caller, TimeGenerated, Total, baseline, anomalies, score 
  | where Total > TotalEventsThreshold and baseline > 0 
  | join (AzureActivity 
  | where TimeGenerated > startofday(ago(endtime)) 
  | where OperationNameValue endswith "delete" 
  | summarize count(), make_set(OperationNameValue,100), make_set(_ResourceId,100) by bin(TimeGenerated, timeframe), Caller ) on TimeGenerated, Caller 
  | extend Name = iif(Caller has '@',tostring(split(Caller,'@',0)[0]),"")
  | extend UPNSuffix = iif(Caller has '@',tostring(split(Caller,'@',1)[0]),"")
  | extend AadUserId = iif(Caller !has '@',Caller,"")
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Caller
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: AadUserId
version: 2.0.4
kind: Scheduled

Stages and Predicates

Parameters

let starttime = 14d;
let endtime = 1d;
let timeframe = 1d;
let TotalEventsThreshold = 25;

let TimeSeriesData is inlined into the numbered stages below.

Stages 1 to 5 define let TimeSeriesData as the rule's main pipeline source. Stages 6 to 13 use it.

Stage 1: source

AzureActivity

Stage 2: where

| where TimeGenerated between (startofday(ago(starttime))..startofday(now()))

Stage 3: where

| where OperationNameValue endswith "delete"

Stage 4: project

| project TimeGenerated, Caller

The stages below score time-series anomalies (make-series, series_decompose_anomalies).

Stage 5: make-series

| make-series Total = count() on TimeGenerated from startofday(ago(starttime)) to startofday(now()) step timeframe by Caller

Stage 6: extend

TimeSeriesData
| extend (anomalies, score, baseline) = series_decompose_anomalies(Total, 3, -1, 'linefit')

Stage 7: mv-expand

| mv-expand Total to typeof(double), TimeGenerated to typeof(datetime), anomalies to typeof(double), score to typeof(double), baseline to typeof(long)

Stage 8: where

| where TimeGenerated >= startofday(ago(endtime))

Stage 9: where

| where anomalies > 0

Stage 10: project

| project Caller, TimeGenerated, Total, baseline, anomalies, score

Stage 11: where

| where Total > TotalEventsThreshold and baseline > 0

Stage 12: join

| join (AzureActivity 
| where TimeGenerated > startofday(ago(endtime)) 
| where OperationNameValue endswith "delete" 
| summarize count(), make_set(OperationNameValue,100), make_set(_ResourceId,100) by bin(TimeGenerated, timeframe), Caller ) on TimeGenerated, Caller

Stage 13: extend (3 consecutive steps)

extend AadUserId, Name, UPNSuffix

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Callerproject
TimeGeneratedproject
Totalproject
anomaliesproject
baselineproject
scoreproject
Nameextend
UPNSuffixextend
AadUserIdextend

Modified domain federation trust settings

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

This will alert when a user or application modifies the federation settings on the domain or Update domain authentication from Managed to Federated. For example, this alert will trigger when a new Active Directory Federated Service (ADFS) TrustedRealm object, such as a signing certificate, is added to the domain. Modification to domain federation settings should be rare. Confirm the added or modified target domain/URL is legitimate administrator behavior. To understand why an authorized user may update settings for a federated domain in Office 365, Azure, or Intune, see: https://docs.microsoft.com/office365/troubleshoot/active-directory/update-federated-domain-office-365. For details on security realms that accept security tokens, see the ADFS Proxy Protocol (MS-ADFSPP) specification: https://docs.microsoft.com/openspecs/windows_protocols/ms-adfspp/e7b9ea73-1980-4318-96a6-da559486664b. For further information on AuditLogs please see https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities.

MITRE ATT&CK Coverage

TacticTechniques
Persistence
Privilege Escalation
Credential Access

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 95dc4ae3-e0f2-48bd-b996-cdd22b90f9af
name: Modified domain federation trust settings
description: |
  'This will alert when a user or application modifies the federation settings on the domain or Update domain authentication from Managed to Federated.
  For example, this alert will trigger when a new Active Directory Federated Service (ADFS) TrustedRealm object, such as a signing certificate, is added to the domain.
  Modification to domain federation settings should be rare. Confirm the added or modified target domain/URL is legitimate administrator behavior.
  To understand why an authorized user may update settings for a federated domain in Office 365, Azure, or Intune, see: https://docs.microsoft.com/office365/troubleshoot/active-directory/update-federated-domain-office-365.
  For details on security realms that accept security tokens, see the ADFS Proxy Protocol (MS-ADFSPP) specification: https://docs.microsoft.com/openspecs/windows_protocols/ms-adfspp/e7b9ea73-1980-4318-96a6-da559486664b.
  For further information on AuditLogs please see https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities.'
severity: High
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - CredentialAccess
  - Persistence
  - PrivilegeEscalation
relevantTechniques:
  - T1555
  - T1098
query: |
  (union isfuzzy=true
  (
  AuditLogs
  | where OperationName =~ "Set federation settings on domain"
  //| where Result =~ "success"   // commenting out, as it may be interesting to capture failed attempts
  | mv-expand TargetResources
  | extend modifiedProperties = parse_json(TargetResources).modifiedProperties
  | mv-expand modifiedProperties
  | extend targetDisplayName = tostring(parse_json(modifiedProperties).displayName)
  ),
  (
  AuditLogs
  | where OperationName =~ "Set domain authentication"
  //| where Result =~ "success"   // commenting out, as it may be interesting to capture failed attempts
  | mv-expand TargetResources
  | extend modifiedProperties = parse_json(TargetResources).modifiedProperties
  | mv-expand modifiedProperties
  | mv-apply Property = modifiedProperties on
    (
        where Property.displayName =~ "LiveType"
        | extend targetDisplayName = tostring(Property.displayName),
                 NewDomainValue = tostring(Property.newValue)
    )
  | where NewDomainValue has "Federated"
  )
  )
  | mv-apply AdditionalDetail = AdditionalDetails on
    (
        where AdditionalDetail.key =~ "User-Agent"
        | extend UserAgent = tostring(AdditionalDetail.value)
    )
  | extend InitiatingUserOrApp = iff(isnotempty(InitiatedBy.user.userPrincipalName),tostring(InitiatedBy.user.userPrincipalName), tostring(InitiatedBy.app.displayName))
  | extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
  | extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
  | extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
  | extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
  | extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
  | extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
  | project-reorder TimeGenerated, OperationName, InitiatingUserOrApp, AADOperationType, targetDisplayName, Result, InitiatingIpAddress, UserAgent, CorrelationId, TenantId, AADTenantId
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: InitiatingAppName
      - identifier: AadUserId
        columnName: InitiatingAppServicePrincipalId
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: InitiatingUserPrincipalName
      - identifier: Name
        columnName: InitiatingAccountName
      - identifier: UPNSuffix
        columnName: InitiatingAccountUPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: InitiatingAadUserId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: InitiatingIpAddress
version: 1.1.2
kind: Scheduled

Stages and Predicates

Union isfuzzy=true (2 Sources)

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

Leg 1: AuditLogs

AuditLogs
| where OperationName =~ "Set federation settings on domain"
| mv-expand TargetResources
| extend modifiedProperties = parse_json(TargetResources).modifiedProperties
| mv-expand modifiedProperties
| extend targetDisplayName = tostring(parse_json(modifiedProperties).displayName)

Leg 2: AuditLogs

AuditLogs
| where OperationName =~ "Set domain authentication"
| mv-expand TargetResources
| extend modifiedProperties = parse_json(TargetResources).modifiedProperties
| mv-expand modifiedProperties
| mv-apply Property = modifiedProperties on
  (
      where Property.displayName =~ "LiveType"
      | extend targetDisplayName = tostring(Property.displayName),
               NewDomainValue = tostring(Property.newValue)
  )
| where NewDomainValue has "Federated"

Applied to the Combined Result

| mv-apply AdditionalDetail = AdditionalDetails on
  (
      where AdditionalDetail.key =~ "User-Agent"
      | extend UserAgent = tostring(AdditionalDetail.value)
  )
| extend InitiatingUserOrApp = iff(isnotempty(InitiatedBy.user.userPrincipalName),tostring(InitiatedBy.user.userPrincipalName), tostring(InitiatedBy.app.displayName))
| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
| project-reorder TimeGenerated, OperationName, InitiatingUserOrApp, AADOperationType, targetDisplayName, Result, InitiatingIpAddress, UserAgent, CorrelationId, TenantId, AADTenantId

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
AdditionalDetail.keyeq
  • User-Agent transforms: kusto_dynamic_path
field:"AdditionalDetail.key" kind:eq value:"User-Agent"
NewDomainValuematch
  • Federated transforms: term
field:"NewDomainValue" kind:match value:"Federated"
OperationNameeq
  • Set domain authentication corpus 3 (kusto 3)
  • Set federation settings on domain corpus 3 (kusto 3)
field:"OperationName" kind:eq
Property.displayNameeq
  • LiveType transforms: kusto_dynamic_path
field:"Property.displayName" kind:eq value:"LiveType"

Output Fields

These fields are emitted when the rule matches.

FieldSource
modifiedPropertiesextend
targetDisplayNameextend
InitiatingUserOrAppextend
InitiatingAppNameextend
InitiatingAppServicePrincipalIdextend
InitiatingUserPrincipalNameextend
InitiatingAadUserIdextend
InitiatingIpAddressextend
InitiatingAccountNameextend
InitiatingAccountUPNSuffixextend

NGINX - Multiple client errors from single IP address

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

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

MITRE ATT&CK Coverage

Rule Body

id: 42771afe-edb3-4330-bc4a-abf6a5714454
name: NGINX - Multiple client errors from single IP address
description: |
  'Detects multiple client errors from one source in short timeframe'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: CustomLogsAma
    dataTypes:
      - NGINX_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
  - T1133
query: |
  let threshold = 100;
  NGINXHTTPServer
  | where tolong(HttpStatusCode) >= 400 and tolong(HttpStatusCode) <= 499 
  | summarize MultipleClientErrors = count() by SrcIpAddr, bin(TimeGenerated, 5m)
  | where MultipleClientErrors > 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

NGINXHTTPServer

Stage 2: where

| where tolong(HttpStatusCode) >= 400 and tolong(HttpStatusCode) <= 499

Stage 3: summarize

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

Stage 4: where

| where MultipleClientErrors > 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
MultipleClientErrorssummarize
SrcIpAddrsummarize
IPCustomEntityextend

NRT DNS events related to mining pools

#

Outgoing connection attempts stateful anomaly on database

#
Status
available
Severity
medium
Time window
14d
Group by
ApplicationName, ClientIp, Database, HostName, PrincipalName, ResourceId, timeSlice
Source
github.com/Azure/Azure-Sentinel

'This query detects batches of distinct SQL queries that execute (or attempt to) commands that could indicate potential security issues - such as attempts to access external sites or resources (e.g. for downloading malicious content).'

MITRE ATT&CK Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: c105513d-e398-4a02-bd91-54b9b2d6fa7d
name: Outgoing connection attempts stateful anomaly on database
description: |
  'This query detects batches of distinct SQL queries that execute (or attempt to) commands that could indicate potential security issues - such as attempts to access external sites or resources (e.g. for downloading malicious content).'
severity: Medium
requiredDataConnectors:
  - connectorId: AzureSql
    dataTypes:
      - AzureDiagnostics
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - CommandAndControl
relevantTechniques:
  - T1105
  - T1071
tags:
  - SQL
query: |
    let monitoredStatementsThreshold = 1;           // Minimal number of monitored statements in the slice to trigger an anomaly.
    let trainingSlicesThreshold = 5;                // The maximal amount of slices with monitored statements in the training window before anomaly detection is throttled.
    let timeSliceSize = 1h;                         // The size of the single timeSlice for individual aggregation.
    let detectionWindow = 1h;                       // The size of the recent detection window for detecting anomalies.  
    let trainingWindow = detectionWindow + 14d;     // The size of the training window before the detection window for learning the normal state.
    let hotwords = pack_array('http:', 'https:', 'ftp:', 'onion.pet'); // List of monitored hot words.
    let processedData = materialize (
        AzureDiagnostics
        | where TimeGenerated >= ago(trainingWindow)
        | where Category == 'SQLSecurityAuditEvents' and action_id_s has_any ("RCM", "BCM") // Keep only SQL affected rows
        | project TimeGenerated, PrincipalName = server_principal_name_s, ClientIp = client_ip_s, HostName = host_name_s, ResourceId,
                  ApplicationName = application_name_s, ActionName = action_name_s, Database = strcat(LogicalServerName_s, '/', database_name_s),
                  IsSuccess = succeeded_s, AffectedRows = affected_rows_d,
                  ResponseRows = response_rows_d, Statement = statement_s,
                  Error = case( additional_information_s has 'error_code', toint(extract("<error_code>([0-9.]+)", 1, additional_information_s))
                        , additional_information_s has 'failure_reason', toint(extract("<failure_reason>Err ([0-9.]+)", 1, additional_information_s))
                        , 0),
                  State = case( additional_information_s has 'error_state', toint(extract("<error_state>([0-9.]+)", 1, additional_information_s))
                        , additional_information_s has 'failure_reason', toint(extract("<failure_reason>Err ([0-9.]+), Level ([0-9.]+)", 2, additional_information_s))
                        , 0),
                  AdditionalInfo = additional_information_s, timeSlice = floor(TimeGenerated, timeSliceSize)
        | extend hasHotword = iff(Statement has_any (hotwords), 1, 0)
        | summarize countEvents = count(), countStatements = dcount(Statement)
            , countStatementsWithHotwords = dcountif(Statement, hasHotword == 1)
            , countFailedStatementsWithHotwords = dcountif(Statement, (hasHotword == 1) and (Error > 0))
            , countSuccessfulStatementsWithHotwords = dcountif(Statement, ((hasHotword == 1)) and (Error == 0))
            , anyMonitoredStatement = anyif(Statement, (hasHotword == 1))
            , anySuccessfulMonitoredStatement = anyif(Statement, (hasHotword == 1) and (Error == 0))
            , anyInfo = anyif(AdditionalInfo, hasHotword == 1)
            , hotWord = anyif(extract(strcat_array(hotwords, '|'), 0, tolower(Statement)), hasHotword == 1)
            by Database, ClientIp, ApplicationName, PrincipalName, timeSlice,HostName,ResourceId   
        | extend WindowType = case( timeSlice >= ago(detectionWindow), 'detection',
                                               (ago(trainingWindow) <= timeSlice and timeSlice < ago(detectionWindow)), 'training', 'other')
        | where WindowType in ('detection', 'training'));
    let trainingSet =
        processedData
        | where WindowType == 'training'
        | summarize countSlicesWithHotwords = dcountif(timeSlice, countStatementsWithHotwords >= monitoredStatementsThreshold)
            by Database;
    processedData
    | where WindowType == 'detection' 
    | join kind = inner (trainingSet) on Database
    | extend IsHotwordAnomalyOnStatement = iff(((countStatementsWithHotwords >= monitoredStatementsThreshold) and (countSlicesWithHotwords <= trainingSlicesThreshold)), true, false)
        , anomalyScore = round(countStatementsWithHotwords/monitoredStatementsThreshold, 0)
    | where IsHotwordAnomalyOnStatement == 'true'
    | project TimeGenerated = timeSlice, Database, ClientIp, ApplicationName, PrincipalName, HostName, ResourceId, countEvents, countStatements, countStatementsWithHotwords, anyMonitoredStatement, anyInfo, anomalyScore, hotWord
    | extend Name = tostring(split(PrincipalName,'@',0)[0]), UPNSuffix = tostring(split(PrincipalName,'@',1)[0])    
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIp
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: ApplicationName
  - entityType: AzureResource
    fieldMappings:
      - identifier: ResourceId
        columnName: ResourceId
customDetails:
  HotWords: hotWord
alertDetailsOverride:
  alertDisplayNameFormat: 'Outgoing connection attempts stateful anomaly on database {{Database}}'
  alertDescriptionFormat: 'An anomaly was detected on database {{Database}} with {{countStatementsWithHotwords}} statements with monitored hot words (http:, https:, ftp:, onion.pet etc.) in the last hour. Investigate the database activity for potential malicious attempts to access external sites or resources.'
version: 1.1.2
kind: Scheduled

Stages and Predicates

Parameters

let monitoredStatementsThreshold = 1;
let trainingSlicesThreshold = 5;
let timeSliceSize = 1h;
let detectionWindow = 1h;
let trainingWindow = detectionWindow + 14d;
let hotwords = pack_array('http:', 'https:', 'ftp:', 'onion.pet');

let processedData is inlined into the numbered stages below.

Let Binding: trainingSet used in Stage 10

let trainingSet = processedData
    | where WindowType == 'training'
    | summarize countSlicesWithHotwords = dcountif(timeSlice, countStatementsWithHotwords >= monitoredStatementsThreshold)
        by Database;

Stages 1 to 8 define let processedData as the rule's main pipeline source. Stages 9 to 14 use it.

Stage 1: source

AzureDiagnostics

Stage 2: where

| where TimeGenerated >= ago(trainingWindow)

Stage 3: where

| where Category == 'SQLSecurityAuditEvents' and action_id_s has_any ("RCM", "BCM")

Stage 4: project

| project TimeGenerated, PrincipalName = server_principal_name_s, ClientIp = client_ip_s, HostName = host_name_s, ResourceId,
              ApplicationName = application_name_s, ActionName = action_name_s, Database = strcat(LogicalServerName_s, '/', database_name_s),
              IsSuccess = succeeded_s, AffectedRows = affected_rows_d,
              ResponseRows = response_rows_d, Statement = statement_s,
              Error = case( additional_information_s has 'error_code', toint(extract("<error_code>([0-9.]+)", 1, additional_information_s))
                    , additional_information_s has 'failure_reason', toint(extract("<failure_reason>Err ([0-9.]+)", 1, additional_information_s))
                    , 0),
              State = case( additional_information_s has 'error_state', toint(extract("<error_state>([0-9.]+)", 1, additional_information_s))
                    , additional_information_s has 'failure_reason', toint(extract("<failure_reason>Err ([0-9.]+), Level ([0-9.]+)", 2, additional_information_s))
                    , 0),
              AdditionalInfo = additional_information_s, timeSlice = floor(TimeGenerated, timeSliceSize)

Stage 5: extend

| extend hasHotword = iff(Statement has_any (hotwords), 1, 0)
hasHotword =
ifStatement has "hotwords"1
else0

Stage 6: summarize

| summarize countEvents = count(), countStatements = dcount(Statement)
        , countStatementsWithHotwords = dcountif(Statement, hasHotword == 1)
        , countFailedStatementsWithHotwords = dcountif(Statement, (hasHotword == 1) and (Error > 0))
        , countSuccessfulStatementsWithHotwords = dcountif(Statement, ((hasHotword == 1)) and (Error == 0))
        , anyMonitoredStatement = anyif(Statement, (hasHotword == 1))
        , anySuccessfulMonitoredStatement = anyif(Statement, (hasHotword == 1) and (Error == 0))
        , anyInfo = anyif(AdditionalInfo, hasHotword == 1)
        , hotWord = anyif(extract(strcat_array(hotwords, '|'), 0, tolower(Statement)), hasHotword == 1)
        by Database, ClientIp, ApplicationName, PrincipalName, timeSlice,HostName,ResourceId

Stage 7: extend

| extend WindowType = case( timeSlice >= ago(detectionWindow), 'detection',
                                           (ago(trainingWindow) <= timeSlice and timeSlice < ago(detectionWindow)), 'training', 'other')
WindowType =
if/* macro: (timeSlice >= ago(detectionWindow)) */'detection'
elif/* macro: (ago(trainingWindow) <= timeSlice) */'training'
else'other'

Stage 8: where

| where WindowType in ('detection', 'training')

Stage 9: where

processedData
| where WindowType == 'detection'

Stage 10: join

| join kind = inner (trainingSet) on Database

Stage 11: extend

extend IsHotwordAnomalyOnStatement, anomalyScore
IsHotwordAnomalyOnStatement =
ifcountStatementsWithHotwords >= monitoredStatementsThreshold and countSlicesWithHotwords <= trainingSlicesThresholdtrue
elsefalse

Stage 12: where

where IsHotwordAnomalyOnStatement == true

Stage 13: project

project ApplicationName, ClientIp, Database, HostName, PrincipalName, ResourceId, TimeGenerated, anomalyScore, anyInfo, anyMonitoredStatement, countEvents, countStatements, countStatementsWithHotwords, hotWord

Stage 14: extend

extend Name, UPNSuffix

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
ApplicationNameproject
ClientIpproject
Databaseproject
HostNameproject
PrincipalNameproject
ResourceIdproject
TimeGeneratedproject
anomalyScoreproject
anyInfoproject
anyMonitoredStatementproject
countEventsproject
countStatementsproject
countStatementsWithHotwordsproject
hotWordproject
Nameextend
UPNSuffixextend

Pathlock TDnR - Debitor Change Documents

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

Detects changes to debitor (customer) master records in SAP, forwarded by Pathlock Threat Detection and Response. Unauthorized modifications may indicate fraudulent manipulation of customer accounts or receivables data.

MITRE ATT&CK Coverage

TacticTechniques
Impact

Rule Body

id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c08
name: Pathlock TDnR - Debitor Change Documents
kind: Scheduled
description: >-
  Detects changes to debitor (customer) master records in SAP, forwarded by Pathlock Threat Detection
  and Response. Unauthorized modifications may indicate fraudulent manipulation of customer
  accounts or receivables data.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Pathlock_TDnR
    dataTypes:
      - Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1565
query: |
  Pathlock_TDnR_CL
  | where DataSource == "CHANGEDOC_DEBI"
  | 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 == "CHANGEDOC_DEBI"

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

Pathlock TDnR - SAP HANA Database Audit Trail

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

Detects security events from the SAP HANA tenant database audit trail, forwarded by Pathlock Threat Detection and Response. HANA audit anomalies may indicate unauthorized database access, privilege abuse, or attempts to read sensitive data directly from the HANA database.

MITRE ATT&CK Coverage

TacticTechniques
Initial Access
Discovery

Rule Body

id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c27
name: Pathlock TDnR - SAP HANA Database Audit Trail
kind: Scheduled
description: >-
  Detects security events from the SAP HANA tenant database audit trail, forwarded by Pathlock Threat
  Detection and Response. HANA audit anomalies may indicate unauthorized database access, privilege
  abuse, or attempts to read sensitive data directly from the HANA database.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Pathlock_TDnR
    dataTypes:
      - Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
  - CredentialAccess
  - InitialAccess
relevantTechniques:
  - T1082
  - T1078
query: |
  Pathlock_TDnR_CL
  | where DataSource == "HANA_AUDIT_TRAIL"
  | 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 == "HANA_AUDIT_TRAIL"

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

Privileged User Logon from new ASN

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

Detects a successful logon by a privileged account from an ASN not logged in from in the last 14 days. Monitor these logons to ensure they are legitimate and identify if there are any similar sign ins.

MITRE ATT&CK Coverage

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 55073036-bb86-47d3-a85a-b113ac3d9396
name: Privileged User Logon from new ASN
description: |
  'Detects a successful logon by a privileged account from an ASN not logged in from in the last 14 days.
    Monitor these logons to ensure they are legitimate and identify if there are any similar sign ins.'
severity: Medium
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
  - connectorId: BehaviorAnalytics
    dataTypes:
      - BehaviorAnalytics
  - connectorId: BehaviorAnalytics
    dataTypes:
      - IdentityInfo
queryFrequency: 1d
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1078.004
tags:
  - AADSecOpsGuide
query: |
  let admins=(IdentityInfo
    | where AssignedRoles contains "admin" or GroupMembership has "Admin"
    | summarize by tolower(AccountUPN));
    let known_asns = (
    SigninLogs
    | where TimeGenerated between(ago(14d)..ago(1d))
    | where ResultType == 0
    | summarize by AutonomousSystemNumber);
    SigninLogs
    | where TimeGenerated > ago(1d)
    | where ResultType == 0
    | where tolower(UserPrincipalName) in (admins)
    | where AutonomousSystemNumber !in (known_asns)
    | project-reorder TimeGenerated, UserPrincipalName, UserAgent, IPAddress, AutonomousSystemNumber
    | extend AccountName = tostring(split(UserPrincipalName, "@")[0]), AccountUPNSuffix = tostring(split(UserPrincipalName, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
version: 1.0.6
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Identity", "Security - Others" ]

Stages and Predicates

Let Binding: admins used in Stages 1, 5

let admins = (IdentityInfo
  | where AssignedRoles contains "admin" or GroupMembership has "Admin"
  | summarize by tolower(AccountUPN));

Let Binding: known_asns used in Stage 6

let known_asns = (
  SigninLogs
  | where TimeGenerated between(ago(14d)..ago(1d))
  | where ResultType == 0
  | summarize by AutonomousSystemNumber);

Stage 1: source

let admins

Stage 2: source

SigninLogs

Stage 3: where

where TimeGenerated > ago(86400s)

Stage 4: where

where ResultType == 0

Stage 5: where

where UserPrincipalName == "admins"

Stage 6: where

where not (AutonomousSystemNumber == "known_asns")

Stage 7: project-reorder

project-reorder

Stage 8: extend

extend AccountName, AccountUPNSuffix

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
AutonomousSystemNumbereqknown_asnsexcludes:AutonomousSystemNumber field:"AutonomousSystemNumber" value:"known_asns"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountNameextend
AccountUPNSuffixextend

Radiflow - Network Scanning Detected

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

'Generates an incident when a network scan is detected either by Radiflow's iSID.'

MITRE ATT&CK Coverage

Rule Body

id: cde00cc5-5841-4aa9-96c5-dd836f9e3f26
name: Radiflow - Network Scanning Detected
description: |
  'Generates an incident when a network scan is detected either by Radiflow's iSID.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: RadiflowIsid
    dataTypes:
      - RadiflowEvent
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Discovery
relevantTechniques:
- T0840
- T0846
- T0888
query: |
  RadiflowEvent
  | where DeviceProduct =~ 'iSID'
  | where
      (
          EventClassID in (52, 105, 150, 151, 152, 153, 158, 166, 175)
          or EventMessage has 'Scan'
      )
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AllEntities
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: 'Network Scanning Detected: {{EventMessage}}'
  alertDescriptionFormat: "A suspicious network scan activity has been detected in the network. Please check the the alert details for further information.\n\nMessage: {{EventMessage}}\nSource device: {{SourceIP}}\nDestination device (if any): {{DestinationIP}} "
  alertSeverityColumnName: EventSeverity
  alertDynamicProperties: []
suppressionEnabled: false
suppressionDuration: 5h
customDetails:
  SourceMAC: SourceMACAddress
  SourceType: SourceType
  SourceVendor: SourceVendor
  SourceVLAN: SourceVLAN
  DestinationMAC: DestinationMACAddress
  DestinationType: DestinationType
  DestinationVendor: DestinationVendor
  Port: Port
  Protocol: Protocol
  SourceHostName: SourceHostName
  SourceIP: SourceIP
  DestinationHostName: DestinationHostName
  DestinationIP: DestinationIP
entityMappings:
- entityType: Host
  fieldMappings:
  - identifier: HostName
    columnName: SourceHostName
  - identifier: NetBiosName
    columnName: SourceHostName
- entityType: Host
  fieldMappings:
  - identifier: HostName
    columnName: DestinationHostName
  - identifier: NetBiosName
    columnName: DestinationHostName
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: SourceIP
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: DestinationIP
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

RadiflowEvent

Stage 2: where

| where DeviceProduct =~ 'iSID'

Stage 3: where

| where
    (
        EventClassID in (52, 105, 150, 151, 152, 153, 158, 166, 175)
        or EventMessage has 'Scan'
    )

Indicators

These rows show field, operator, and value matches.

Snowflake - Possible discovery activity

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

'Detects possible discovery activity.'

MITRE ATT&CK Coverage

TacticTechniques
Discovery

Rule Body

id: 09b8dfc7-87b0-4215-b34b-bab363d685cb
name: Snowflake - Possible discovery activity
description: |
  'Detects possible discovery activity.'
severity: Medium
status : Available
requiredDataConnectors:
  - connectorId: Snowflake
    dataTypes:
      - Snowflake
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
relevantTechniques:
  - T1526
query: |
  let threshold = 20;
  Snowflake
  | where QueryType =~ 'SHOW'
  | where QueryExecutionStatus =~ 'SUCCESS'
  | summarize count() by TargetUsername, bin(TimeGenerated, 5m)
  | where count_ > threshold
  | extend AccountCustomEntity = TargetUsername
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 20;

Stage 1: source

Snowflake

Stage 2: where

| where QueryType =~ 'SHOW'

Stage 3: where

| where QueryExecutionStatus =~ 'SUCCESS'

Stage 4: summarize

| summarize count() by TargetUsername, bin(TimeGenerated, 5m)

Stage 5: where

| where count_ > threshold

Stage 6: extend

| extend AccountCustomEntity = TargetUsername

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
TargetUsernamesummarize
AccountCustomEntityextend

Tailscale Premium: Network flow beaconing detected

#
Status
available
Severity
medium
Time window
2d
Group by
DeltaSec, Dst, DstNodeName, DstUser, Proto, Src, SrcNodeName, SrcUser
Source
github.com/Azure/Azure-Sentinel

Identifies when flows between a src-dst pair recur at a regular interval (80%+ of inter-flow gaps cluster on the same delta over 10+ flows). Signature of C2 beaconing or scheduled exfiltration. Requires Tailscale Premium or Enterprise.

MITRE ATT&CK Coverage

Rule Body

id: e3f4a5b6-3c4d-5e6f-7a8b-9c0d1e2f3a4b
name: "Tailscale Premium: Network flow beaconing detected"
description: |
  Identifies when flows between a src-dst pair recur at a regular interval (80%+ of inter-flow gaps cluster on the same delta over 10+ flows). Signature of C2 beaconing or scheduled exfiltration. Requires Tailscale Premium or Enterprise.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: TailscalePremiumCCF
    dataTypes:
      - Tailscale_Network_CL
queryFrequency: 1h
queryPeriod: 2d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
  - Exfiltration
relevantTechniques:
  - T1071
  - T1095
  - T1029
query: |
  let lookback = 2d;
  let minFlows = 10;
  let beaconPercentThreshold = 80.0;
  Tailscale_Network_CL
  | where TimeGenerated > ago(lookback)
  | where HasVirtualTraffic
  | mv-expand t = VirtualTraffic
  | extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto)
  | project TimeGenerated, Src, Dst, Proto, SrcNodeName, SrcUser, DstNodeName, DstUser
  | sort by Src asc, Dst asc, Proto asc, TimeGenerated asc
  | serialize
  | extend NextTime = next(TimeGenerated), NextSrc = next(Src), NextDst = next(Dst), NextProto = next(Proto)
  | where Src == NextSrc and Dst == NextDst and Proto == NextProto
  | extend DeltaSec = datetime_diff('second', NextTime, TimeGenerated)
  | where DeltaSec > 5
  | summarize DeltaCount = count() by Src, Dst, Proto, DeltaSec, SrcNodeName, SrcUser, DstNodeName, DstUser
  | summarize (MostFrequentDeltaCount, MostFrequentDeltaSec) = arg_max(DeltaCount, DeltaSec), TotalFlows = sum(DeltaCount) by Src, Dst, Proto, SrcNodeName, SrcUser, DstNodeName, DstUser
  | where TotalFlows >= minFlows
  | extend BeaconPercent = round(MostFrequentDeltaCount * 100.0 / TotalFlows, 1)
  | where BeaconPercent >= beaconPercentThreshold
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: SrcNodeName
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: DstNodeName
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: SrcUser
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: Src
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: P1D
    matchingMethod: AllEntities
kind: Scheduled
version: 1.0.0

Stages and Predicates

Parameters

let lookback = 2d;
let minFlows = 10;
let beaconPercentThreshold = 80.0;

Stage 1: source

Tailscale_Network_CL

Stage 2: where

| where TimeGenerated > ago(lookback)

Stage 3: where

| where HasVirtualTraffic

Stage 4: mv-expand

| mv-expand t = VirtualTraffic

Stage 5: extend

| extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto)

Stage 6: project

| project TimeGenerated, Src, Dst, Proto, SrcNodeName, SrcUser, DstNodeName, DstUser

Stage 7: sort

| sort by Src asc, Dst asc, Proto asc, TimeGenerated asc

Stage 8: kusto:serialize

| serialize

Stage 9: extend

| extend NextTime = next(TimeGenerated), NextSrc = next(Src), NextDst = next(Dst), NextProto = next(Proto)

Stage 10: where

| where Src == NextSrc and Dst == NextDst and Proto == NextProto

Stage 11: extend

| extend DeltaSec = datetime_diff('second', NextTime, TimeGenerated)

Stage 12: where

| where DeltaSec > 5

Stage 13: summarize

| summarize DeltaCount = count() by Src, Dst, Proto, DeltaSec, SrcNodeName, SrcUser, DstNodeName, DstUser

Stage 14: summarize

| summarize (MostFrequentDeltaCount, MostFrequentDeltaSec) = arg_max(DeltaCount, DeltaSec), TotalFlows = sum(DeltaCount) by Src, Dst, Proto, SrcNodeName, SrcUser, DstNodeName, DstUser

Stage 15: where

| where TotalFlows >= minFlows

Stage 16: extend

| extend BeaconPercent = round(MostFrequentDeltaCount * 100.0 / TotalFlows, 1)

Stage 17: where

| where BeaconPercent >= beaconPercentThreshold

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Dstsummarize
DstNodeNamesummarize
DstUsersummarize
MostFrequentDeltaCountsummarize
MostFrequentDeltaSecsummarize
Protosummarize
Srcsummarize
SrcNodeNamesummarize
SrcUsersummarize
TotalFlowssummarize
BeaconPercentextend

Theom Low Risks

#

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
5m
Source
github.com/Azure/Azure-Sentinel

"Creates Microsoft Sentinel incidents for low risk Theom alerts"

MITRE ATT&CK Coverage

Rule Body

id: cf7fb616-ac80-40ce-ad18-aa18912811f8
name: Theom Low Risks
description: |
  "Creates Microsoft Sentinel incidents for low risk Theom alerts"
severity: High
status : Available
requiredDataConnectors: 
  - connectorId: Theom
    dataTypes:
      - TheomAlerts_CL
queryPeriod: 5m
queryFrequency: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Collection
  - CommandAndControl
  - CredentialAccess
  - DefenseEvasion
  - Discovery
  - Exfiltration
  - Impact
  - Reconnaissance
relevantTechniques:
  - T1592
  - T1589
  - T1070
  - T1552
  - T1619
  - T1119
  - T1560
  - T1530
  - T1213
  - T1001
  - T1041
  - T1537
  - T1485
  - T1486
  - T1565
query: |
  TheomAlerts_CL
   | where priority_s == "P4"
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: "Theom Alert ID: {{id_s}} "
  alertDescriptionFormat: "\nSummary: {{summary_s}}  \nAdditional info: {{details_s}}\nPlease investigate further on Theom UI at {{deepLink_s}}\n"
entityMappings:
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: customProps_AssetName_s
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: deepLink_s
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

TheomAlerts_CL

Stage 2: where

| where priority_s == "P4"

Indicators

These rows show field, operator, and value matches.

TI map IP entity to GitHub_CL

#
Severity
medium
Time window
14d
Group by
$left.TI_ipEntity, $right.IPaddress, IndicatorId
Source
github.com/Azure/Azure-Sentinel

'Identifies a match in GitHub_CL table from any IP IOC from TI'

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: aac495a9-feb1-446d-b08e-a1164a539452
name: TI map IP entity to GitHub_CL
description: |
  'Identifies a match in GitHub_CL table from any IP IOC from TI'
severity: Medium
requiredDataConnectors:
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: ThreatIntelligenceTaxii
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: MicrosoftDefenderThreatIntelligence
    dataTypes:
      - ThreatIntelligenceIndicator
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
query: |
  let dt_lookBack = 1h; // Look back 1 hour for VMConnection events
  let ioc_lookBack = 14d; // Look back 14 days for threat intelligence indicators
  ThreatIntelligenceIndicator
  // Picking up only IOC's that contain the entities we want
  | where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
  | where Action == true
  | where TimeGenerated >= ago(ioc_lookBack)
  // Taking the first non-empty value based on potential IOC match availability
  | extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
  | extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
  | extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
  | where Active == true and ExpirationDateTime > now()
  | join (
    GitHubAudit
    | where TimeGenerated >= ago(dt_lookBack)
    | extend GitHubAudit_TimeGenerated = TimeGenerated
  )
  on $left.TI_ipEntity == $right.IPaddress
  | project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, GitHubAudit_TimeGenerated, TI_ipEntity, IPaddress, Actor, Action, Country, OperationType, NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress

entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Actor
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPaddress
version: 1.0.5
kind: Scheduled

Stages and Predicates

Parameters

let dt_lookBack = 1h;
let ioc_lookBack = 14d;

Stage 1: source

ThreatIntelligenceIndicator

Stage 2: where

| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)

Stage 3: where

| where Action == true

Stage 4: where

| where TimeGenerated >= ago(ioc_lookBack)

Stage 5: extend (3 consecutive steps)

| extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
TI_ipEntity =
ifisnotempty(NetworkIP)NetworkIP
elseNetworkDestinationIP

Stage 6: summarize

| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId

Stage 7: where

| where Active == true and ExpirationDateTime > now()

Stage 8: join

| join (
  GitHubAudit
  | where TimeGenerated >= ago(dt_lookBack)
  | extend GitHubAudit_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.IPaddress

Stage 9: project

project Action, ActivityGroupNames, Actor, ConfidenceScore, Country, Description, EmailSourceIpAddress, ExpirationDateTime, GitHubAudit_TimeGenerated, IPaddress, IndicatorId, LatestIndicatorTime, NetworkDestinationIP, NetworkIP, NetworkSourceIP, OperationType, TI_ipEntity, ThreatType, Url

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Actionproject
ActivityGroupNamesproject
Actorproject
ConfidenceScoreproject
Countryproject
Descriptionproject
EmailSourceIpAddressproject
ExpirationDateTimeproject
GitHubAudit_TimeGeneratedproject
IPaddressproject
IndicatorIdproject
LatestIndicatorTimeproject
NetworkDestinationIPproject
NetworkIPproject
NetworkSourceIPproject
OperationTypeproject
TI_ipEntityproject
ThreatTypeproject
Urlproject

Tomcat - Known malicious user agent

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

'Detects known malicious user agents'

MITRE ATT&CK Coverage

Rule Body

id: 5e77a818-5825-4ff6-a901-80891c4774d1
name: Tomcat - Known malicious user agent
description: |
  'Detects known malicious user agents'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: CustomLogsAma
    datatypes:
      - Tomcat_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
  - T1133
query: |
  let malicious_ua = dynamic(['Nikto', 'hydra', '.nasl', 'absinthe', 'advanced email extractor', 'arachni', 'autogetcontent', 'bilbo', 'BFAC', 'brutus', 'brutus/aet', 'bsqlbf', 'cgichk', 'cisco-torch', 'commix', 'core-project', 'crimscanner', 'datacha0s', 'dirbuster', 'domino hunter', 'dotdotpwn', 'email extractor', 'fhscan core', 'floodgate', 'get-minimal', 'gootkit auto-rooter scanner', 'grabber', 'grendel-scan', 'havij', 'inspath', 'internet ninja', 'jaascois', 'zmeu', 'masscan', 'metis', 'morfeus', 'mysqloit', 'n-stealth', 'nessus', 'netsparker', 'nmap nse', 'nmap scripting engine', 'nmap-nse', 'nsauditor', 'openvas', 'pangolin', 'paros', 'pmafind', 'prog.customcrawler', 'qualys was', 's.t.a.l.k.e.r.', 'security scan', 'springenwerk', 'sql power injector', 'sqlmap', 'sqlninja', 'teh forest lobster', 'this is an exploit', 'toata dragostea', 'toata dragostea mea pentru diavola', 'uil2pn', 'vega', 'voideye', 'w3af.sf.net', 'w3af.sourceforge.net', 'w3af.org', 'webbandit', 'webinspect', 'webshag', 'webtrends security analyzer', 'webvulnscan', 'whatweb', 'whcc', 'wordpress hash grabber', 'xmlrpc exploit', 'WPScan', 'XSpider', 'SF/', 'FooBar/42', 'ScanAlert', 'Webscanner', 'Webster', 'fantomCrew', 'fantomBrowser', 'visvo', 'magereport', 'ltx71', 'websiteprotection', 'BigCliqueBOT', 'BOT for JCE']);
  TomcatEvent
  | where HttpUserAgentOriginal has_any (malicious_ua)
  | extend MalwareCustomEntity = HttpUserAgentOriginal
entityMappings:
  - entityType: Malware
    fieldMappings:
      - identifier: Name
        columnName: MalwareCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Let Binding: malicious_ua used in Stage 2

let malicious_ua = dynamic(['Nikto', 'hydra', '.nasl', 'absinthe', 'advanced email extractor', 'arachni', 'autogetcontent', 'bilbo', 'BFAC', 'brutus', 'brutus/aet', 'bsqlbf', 'cgichk', 'cisco-torch', 'commix', 'core-project', 'crimscanner', 'datacha0s', 'dirbuster', 'domino hunter', 'dotdotpwn', 'email extractor', 'fhscan core', 'floodgate', 'get-minimal', 'gootkit auto-rooter scanner', 'grabber', 'grendel-scan', 'havij', 'inspath', 'internet ninja', 'jaascois', 'zmeu', 'masscan', 'metis', 'morfeus', 'mysqloit', 'n-stealth', 'nessus', 'netsparker', 'nmap nse', 'nmap scripting engine', 'nmap-nse', 'nsauditor', 'openvas', 'pangolin', 'paros', 'pmafind', 'prog.customcrawler', 'qualys was', 's.t.a.l.k.e.r.', 'security scan', 'springenwerk', 'sql power injector', 'sqlmap', 'sqlninja', 'teh forest lobster', 'this is an exploit', 'toata dragostea', 'toata dragostea mea pentru diavola', 'uil2pn', 'vega', 'voideye', 'w3af.sf.net', 'w3af.sourceforge.net', 'w3af.org', 'webbandit', 'webinspect', 'webshag', 'webtrends security analyzer', 'webvulnscan', 'whatweb', 'whcc', 'wordpress hash grabber', 'xmlrpc exploit', 'WPScan', 'XSpider', 'SF/', 'FooBar/42', 'ScanAlert', 'Webscanner', 'Webster', 'fantomCrew', 'fantomBrowser', 'visvo', 'magereport', 'ltx71', 'websiteprotection', 'BigCliqueBOT', 'BOT for JCE']);

Stage 1: source

TomcatEvent

Stage 2: where

| where HttpUserAgentOriginal has_any (malicious_ua)

Stage 3: extend

| extend MalwareCustomEntity = HttpUserAgentOriginal

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
HttpUserAgentOriginalmatch
  • .nasl transforms: term
  • BFAC transforms: term
  • BOT for JCE transforms: term
  • BigCliqueBOT transforms: term
  • FooBar/42 transforms: term
  • Nikto transforms: term
  • SF/ transforms: term
  • ScanAlert transforms: term
  • WPScan transforms: term
  • Webscanner transforms: term
  • Webster transforms: term
  • XSpider transforms: term
  • absinthe transforms: term
  • advanced email extractor transforms: term
  • arachni transforms: term
  • autogetcontent transforms: term
  • bilbo transforms: term
  • brutus transforms: term
  • brutus/aet transforms: term
  • bsqlbf transforms: term
  • cgichk transforms: term
  • cisco-torch transforms: term
  • commix transforms: term
  • core-project transforms: term
  • crimscanner transforms: term
  • datacha0s transforms: term
  • dirbuster transforms: term
  • domino hunter transforms: term
  • dotdotpwn transforms: term
  • email extractor transforms: term
  • fantomBrowser transforms: term
  • fantomCrew transforms: term
  • fhscan core transforms: term
  • floodgate transforms: term
  • get-minimal transforms: term
  • gootkit auto-rooter scanner transforms: term
  • grabber transforms: term
  • grendel-scan transforms: term
  • havij transforms: term
  • hydra transforms: term
  • +50 more values (see full rule source)
field:"HttpUserAgentOriginal" kind:match

Output Fields

These fields are emitted when the rule matches.

FieldSource
MalwareCustomEntityextend

Veeam ONE Suspicious Incremental Backup Size

#

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
5m
Source
github.com/Azure/Azure-Sentinel

Detects suspiciously large incremental backup sizes.

Rule Body

id: 8bb38c66-ca2a-4ad0-9b49-1e60368f8a19
name: Veeam ONE Suspicious Incremental Backup Size
description: Detects suspiciously large incremental backup sizes.
severity: High
status: Available
requiredDataConnectors:
- connectorId: VeeamCustomTablesDataConnector
  dataTypes:
  - VeeamOneTriggeredAlarms_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: VeeamOneTriggeredAlarms_CL | where PredefinedAlarmId == 364
version: 1.0.1
kind: Scheduled
customDetails:
  PredefinedAlarmId: PredefinedAlarmId
  TriggeredAlarmId: TriggeredAlarmId
  Description: Description
  Name: Name
  VoneHostName: VoneHostName
  TriggeredTime: TriggeredTime
  Status: Status
  Comment: Comment
  ObjectId: ObjectId
  ObjectName: ObjectName
  ObjectType: ObjectType

Stages and Predicates

Stage 1: source

VeeamOneTriggeredAlarms_CL

Stage 2: where

| where PredefinedAlarmId == 364

Indicators

These rows show field, operator, and value matches.

VMware SD-WAN Edge - IDS/IPS Signature Update Succeeded

#
Severity
informational
Time window
1h
Source
github.com/Azure/Azure-Sentinel

The VMware SD-WAN Edge Management Plane reported a successful IDS/IPS signature update. New signatures might impact Data Plane traffic, therefore an audit event is generated.

Rule Body

id: 6364be84-9f13-4fd8-8b4a-8ccb43a89376
name: VMware SD-WAN Edge - IDS/IPS Signature Update Succeeded
version: 1.0.0
kind: Scheduled
description: The VMware SD-WAN Edge Management Plane reported a successful IDS/IPS signature update. New signatures might impact Data Plane traffic, therefore an audit event is generated.
severity: Informational
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
requiredDataConnectors:
  - connectorId: VMwareSDWAN
    dataTypes:
      - SDWAN
query: |+
  VMware_VECO_EventLogs_CL
  | where event == "MGD_ATPUP_APPLY_IDPS_SIGNATURE_SUCCEEDED"
  | extend idpsSignatureVersion = extract("\"version\":\"([0-9]+)\"", 1, tostring(todynamic(detail).data))
  | extend todynamic(detail).edgeSerialNumber
  | extend todynamic(detail).data
  | project-rename idpsSignatureData = detail_data
  | project-rename edgeSerialNumber = detail_edgeSerialNumber
  | project-away detail

suppressionEnabled: false
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: 5h
    matchingMethod: AllEntities
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDescriptionFormat: '{{message}} '
  alertDynamicProperties: []
customDetails:
  idpsSignatureVersion: idpsSignatureVersion
  edgeSerialNumber: edgeSerialNumber
suppressionDuration: 5h

Stages and Predicates

Stage 1: source

VMware_VECO_EventLogs_CL

Stage 2: where

| where event == "MGD_ATPUP_APPLY_IDPS_SIGNATURE_SUCCEEDED"

Stage 3: extend (3 consecutive steps)

| extend idpsSignatureVersion = extract("\"version\":\"([0-9]+)\"", 1, tostring(todynamic(detail).data))
| extend todynamic(detail).edgeSerialNumber
| extend todynamic(detail).data

Stage 4: project-rename

| project-rename idpsSignatureData = detail_data

Stage 5: project-rename

| project-rename edgeSerialNumber = detail_edgeSerialNumber

Stage 6: project-away

| project-away detail

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
eventeq
  • MGD_ATPUP_APPLY_IDPS_SIGNATURE_SUCCEEDED
field:"event" kind:eq value:"MGD_ATPUP_APPLY_IDPS_SIGNATURE_SUCCEEDED"

Output Fields

These fields are emitted when the rule matches.

FieldSource
idpsSignatureVersionextend
idpsSignatureDataproject-rename
edgeSerialNumberproject-rename

Back to all Kusto rules