Detection rules › Kusto
Anomalous User Agent connection attempt
'Identifies connection attempts (success or fail) from clients with very short or very long User Agent strings and with less than 100 connection attempts.'
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | T1190 Exploit Public-Facing Application |
Rule body kusto
id: f845881e-2500-44dc-8ed7-b372af3e1e25
name: Anomalous User Agent connection attempt
description: |
'Identifies connection attempts (success or fail) from clients with very short or very long User Agent strings and with less than 100 connection attempts.'
severity: Low
requiredDataConnectors:
- connectorId: AzureMonitor(IIS)
dataTypes:
- W3CIISLog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
query: |
let short_uaLength = 5;
let long_uaLength = 1000;
let c_threshold = 100;
W3CIISLog
// Exclude local IPs as these create noise
| where cIP !startswith "192.168." and cIP != "::1"
| where isnotempty(csUserAgent) and csUserAgent !in~ ("-", "MSRPC") and (string_size(csUserAgent) <= short_uaLength or string_size(csUserAgent) >= long_uaLength)
| extend csUserAgent_size = string_size(csUserAgent)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), ConnectionCount = count() by Computer, sSiteName, sPort, csUserAgent, csUserAgent_size, csUserName , csMethod, csUriStem, sIP, cIP, scStatus, scSubStatus, scWin32Status
| where ConnectionCount < c_threshold
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
| extend AccountName = tostring(split(csUserName, "@")[0]), AccountUPNSuffix = tostring(split(csUserName, "@")[1])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: csUserName
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: NTDomain
columnName: HostNameDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: cIP
version: 1.0.3
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Threat Protection" ]
Stages and Predicates
Parameters
let short_uaLength = 5;
let long_uaLength = 1000;
let c_threshold = 100;
Stage 1: source
W3CIISLog
Stage 2: where
| where cIP !startswith "192.168." and cIP != "::1"
Stage 3: where
| where isnotempty(csUserAgent) and csUserAgent !in~ ("-", "MSRPC") and (string_size(csUserAgent) <= short_uaLength or string_size(csUserAgent) >= long_uaLength)
Stage 4: extend
| extend csUserAgent_size = string_size(csUserAgent)
Stage 5: summarize
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), ConnectionCount = count() by Computer, sSiteName, sPort, csUserAgent, csUserAgent_size, csUserName , csMethod, csUriStem, sIP, cIP, scStatus, scSubStatus, scWin32Status
Stage 6: where
| where ConnectionCount < c_threshold
Stage 7: extend (3 consecutive steps)
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
| extend AccountName = tostring(split(csUserName, "@")[0]), AccountUPNSuffix = tostring(split(csUserName, "@")[1])
Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Field | Kind | Excluded values |
|---|---|---|
cIP | starts_with | 192.168. |
csUserAgent | in | -, MSRPC |
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
ConnectionCount | lt |
|
cIP | ne |
|
csUserAgent | ge |
|
csUserAgent | is_not_null | |
csUserAgent | le |
|
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
Computer | summarize |
ConnectionCount | summarize |
EndTimeUtc | summarize |
StartTimeUtc | summarize |
cIP | summarize |
csMethod | summarize |
csUriStem | summarize |
csUserAgent | summarize |
csUserAgent_size | summarize |
csUserName | summarize |
sIP | summarize |
sPort | summarize |
sSiteName | summarize |
scStatus | summarize |
scSubStatus | summarize |
scWin32Status | summarize |
DomainIndex | extend |
HostName | extend |
HostNameDomain | extend |
AccountName | extend |
AccountUPNSuffix | extend |