Analytic rule catalog
New UserAgent observed in last 24 hours
Back
| Id | b725d62c-eb77-42ff-96f6-bdc6745fc6e0 |
| Rulename | New UserAgent observed in last 24 hours |
| Description | Identifies new UserAgents observed in the last 24 hours versus the previous 14 days. This detection extracts words from user agents to build the baseline and determine rareity rather than perform a direct comparison. This avoids FPs caused by version numbers and other high entropy user agent components. These new UserAgents could be benign. However, in normally stable environments, these new UserAgents could provide a starting point for investigating malicious activity. Note: W3CIISLog can be noisy depending on the environment, however OfficeActivity and AWSCloudTrail are usually stable with low numbers of detections. |
| Severity | Low |
| Tactics | InitialAccess CommandAndControl Execution |
| Techniques | T1189 T1071 T1203 |
| Required data connectors | AWS AzureMonitor(IIS) Office365 |
| Kind | Scheduled |
| Query frequency | 1d |
| Query period | 14d |
| Trigger threshold | 0 |
| Trigger operator | gt |
| Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Network%20Threat%20Protection%20Essentials/Analytic%20Rules/NewUserAgentLast24h.yaml |
| Version | 1.0.5 |
| Arm template | b725d62c-eb77-42ff-96f6-bdc6745fc6e0.json |
let starttime = 14d;
let endtime = 1d;
let UserAgentAll =
(union isfuzzy=true
(OfficeActivity
| where TimeGenerated >= ago(starttime)
| where isnotempty(UserAgent)
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent, SourceIP = ClientIP, Account = UserId, Type, RecordType, Operation
),
(
W3CIISLog
| where TimeGenerated >= ago(starttime)
| where isnotempty(csUserAgent)
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent = csUserAgent, SourceIP = cIP, Account = csUserName, Type, sSiteName, csMethod, csUriStem
),
(
AWSCloudTrail
| where TimeGenerated >= ago(starttime)
| where isnotempty(UserAgent)
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent, SourceIP = SourceIpAddress, Account = UserIdentityUserName, Type, EventSource, EventName
))
// remove wordSize blocks of non-numeric hex characters prior to word extraction
| extend UserAgentNoHexAlphas = replace("([A-Fa-f]{4,})", "x", UserAgent)
// once blocks of hex chars are removed, extract wordSize blocks of a-z
| extend Tokens = extract_all("([A-Za-z]{4,})", UserAgentNoHexAlphas)
// concatenate extracted words to create a summarized user agent for baseline and comparison
| extend NormalizedUserAgent = strcat_array(Tokens, "|")
| project-away UserAgentNoHexAlphas, Tokens;
UserAgentAll
| where StartTime >= ago(endtime)
| summarize StartTime = min(StartTime), EndTime = max(EndTime), count() by UserAgent, NormalizedUserAgent, SourceIP, Account, Type, RecordType, Operation, EventSource, EventName, sSiteName, csMethod, csUriStem
| join kind=leftanti
(
UserAgentAll
| where StartTime < ago(endtime)
| summarize by NormalizedUserAgent, SourceIP, Account, Type, RecordType, Operation, EventSource, EventName, sSiteName, csMethod, csUriStem
)
on NormalizedUserAgent
| extend timestamp = StartTime
| extend Name = tostring(split(Account, '@', 0)[0]), UPNSuffix = tostring(split(Account, '@', 1)[0])
entityMappings:
- entityType: Account
fieldMappings:
- columnName: Name
identifier: Name
- columnName: UPNSuffix
identifier: UPNSuffix
- entityType: IP
fieldMappings:
- columnName: SourceIP
identifier: Address
query: |
let starttime = 14d;
let endtime = 1d;
let UserAgentAll =
(union isfuzzy=true
(OfficeActivity
| where TimeGenerated >= ago(starttime)
| where isnotempty(UserAgent)
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent, SourceIP = ClientIP, Account = UserId, Type, RecordType, Operation
),
(
W3CIISLog
| where TimeGenerated >= ago(starttime)
| where isnotempty(csUserAgent)
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent = csUserAgent, SourceIP = cIP, Account = csUserName, Type, sSiteName, csMethod, csUriStem
),
(
AWSCloudTrail
| where TimeGenerated >= ago(starttime)
| where isnotempty(UserAgent)
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent, SourceIP = SourceIpAddress, Account = UserIdentityUserName, Type, EventSource, EventName
))
// remove wordSize blocks of non-numeric hex characters prior to word extraction
| extend UserAgentNoHexAlphas = replace("([A-Fa-f]{4,})", "x", UserAgent)
// once blocks of hex chars are removed, extract wordSize blocks of a-z
| extend Tokens = extract_all("([A-Za-z]{4,})", UserAgentNoHexAlphas)
// concatenate extracted words to create a summarized user agent for baseline and comparison
| extend NormalizedUserAgent = strcat_array(Tokens, "|")
| project-away UserAgentNoHexAlphas, Tokens;
UserAgentAll
| where StartTime >= ago(endtime)
| summarize StartTime = min(StartTime), EndTime = max(EndTime), count() by UserAgent, NormalizedUserAgent, SourceIP, Account, Type, RecordType, Operation, EventSource, EventName, sSiteName, csMethod, csUriStem
| join kind=leftanti
(
UserAgentAll
| where StartTime < ago(endtime)
| summarize by NormalizedUserAgent, SourceIP, Account, Type, RecordType, Operation, EventSource, EventName, sSiteName, csMethod, csUriStem
)
on NormalizedUserAgent
| extend timestamp = StartTime
| extend Name = tostring(split(Account, '@', 0)[0]), UPNSuffix = tostring(split(Account, '@', 1)[0])
id: b725d62c-eb77-42ff-96f6-bdc6745fc6e0
queryFrequency: 1d
status: Available
version: 1.0.5
severity: Low
relevantTechniques:
- T1189
- T1071
- T1203
name: New UserAgent observed in last 24 hours
kind: Scheduled
tactics:
- InitialAccess
- CommandAndControl
- Execution
requiredDataConnectors:
- dataTypes:
- AWSCloudTrail
connectorId: AWS
- dataTypes:
- OfficeActivity
connectorId: Office365
- dataTypes:
- W3CIISLog
connectorId: AzureMonitor(IIS)
description: |
'Identifies new UserAgents observed in the last 24 hours versus the previous 14 days. This detection extracts words from user agents to build the baseline and determine rareity rather than perform a direct comparison. This avoids FPs caused by version numbers and other high entropy user agent components.
These new UserAgents could be benign. However, in normally stable environments, these new UserAgents could provide a starting point for investigating malicious activity.
Note: W3CIISLog can be noisy depending on the environment, however OfficeActivity and AWSCloudTrail are usually stable with low numbers of detections.'
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Network%20Threat%20Protection%20Essentials/Analytic%20Rules/NewUserAgentLast24h.yaml
triggerOperator: gt
triggerThreshold: 0
queryPeriod: 14d
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"apiVersion": "2024-01-01-preview",
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/b725d62c-eb77-42ff-96f6-bdc6745fc6e0')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/b725d62c-eb77-42ff-96f6-bdc6745fc6e0')]",
"properties": {
"alertRuleTemplateName": "b725d62c-eb77-42ff-96f6-bdc6745fc6e0",
"customDetails": null,
"description": "'Identifies new UserAgents observed in the last 24 hours versus the previous 14 days. This detection extracts words from user agents to build the baseline and determine rareity rather than perform a direct comparison. This avoids FPs caused by version numbers and other high entropy user agent components.\nThese new UserAgents could be benign. However, in normally stable environments, these new UserAgents could provide a starting point for investigating malicious activity.\nNote: W3CIISLog can be noisy depending on the environment, however OfficeActivity and AWSCloudTrail are usually stable with low numbers of detections.'\n",
"displayName": "New UserAgent observed in last 24 hours",
"enabled": true,
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "Name",
"identifier": "Name"
},
{
"columnName": "UPNSuffix",
"identifier": "UPNSuffix"
}
]
},
{
"entityType": "IP",
"fieldMappings": [
{
"columnName": "SourceIP",
"identifier": "Address"
}
]
}
],
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Network%20Threat%20Protection%20Essentials/Analytic%20Rules/NewUserAgentLast24h.yaml",
"query": "let starttime = 14d;\nlet endtime = 1d;\nlet UserAgentAll =\n(union isfuzzy=true\n(OfficeActivity\n| where TimeGenerated >= ago(starttime)\n| where isnotempty(UserAgent)\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent, SourceIP = ClientIP, Account = UserId, Type, RecordType, Operation\n),\n(\nW3CIISLog\n| where TimeGenerated >= ago(starttime)\n| where isnotempty(csUserAgent)\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent = csUserAgent, SourceIP = cIP, Account = csUserName, Type, sSiteName, csMethod, csUriStem\n),\n(\nAWSCloudTrail\n| where TimeGenerated >= ago(starttime)\n| where isnotempty(UserAgent)\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserAgent, SourceIP = SourceIpAddress, Account = UserIdentityUserName, Type, EventSource, EventName\n))\n// remove wordSize blocks of non-numeric hex characters prior to word extraction\n| extend UserAgentNoHexAlphas = replace(\"([A-Fa-f]{4,})\", \"x\", UserAgent)\n// once blocks of hex chars are removed, extract wordSize blocks of a-z\n| extend Tokens = extract_all(\"([A-Za-z]{4,})\", UserAgentNoHexAlphas)\n// concatenate extracted words to create a summarized user agent for baseline and comparison\n| extend NormalizedUserAgent = strcat_array(Tokens, \"|\")\n| project-away UserAgentNoHexAlphas, Tokens;\nUserAgentAll\n| where StartTime >= ago(endtime)\n| summarize StartTime = min(StartTime), EndTime = max(EndTime), count() by UserAgent, NormalizedUserAgent, SourceIP, Account, Type, RecordType, Operation, EventSource, EventName, sSiteName, csMethod, csUriStem\n| join kind=leftanti\n(\nUserAgentAll\n| where StartTime < ago(endtime)\n| summarize by NormalizedUserAgent, SourceIP, Account, Type, RecordType, Operation, EventSource, EventName, sSiteName, csMethod, csUriStem\n)\non NormalizedUserAgent\n| extend timestamp = StartTime\n| extend Name = tostring(split(Account, '@', 0)[0]), UPNSuffix = tostring(split(Account, '@', 1)[0])\n",
"queryFrequency": "P1D",
"queryPeriod": "P14D",
"severity": "Low",
"status": "Available",
"subTechniques": [],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"CommandAndControl",
"Execution",
"InitialAccess"
],
"techniques": [
"T1071",
"T1189",
"T1203"
],
"templateVersion": "1.0.5",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}