Analytic rule catalog
PRODAFT USTA - TI map URL to Syslog
Back
| Id | 99ae4d06-d352-4926-a1d7-9c28e25b1313 |
| Rulename | PRODAFT USTA - TI map URL to Syslog |
| Description | Identifies a match in Syslog data from any malicious URL indicator ingested from PRODAFT USTA IoC Threat Intelligence (SourceSystem starting with “PRODAFT USTA”). |
| Severity | Medium |
| Tactics | CommandAndControl |
| Techniques | T1071 |
| Required data connectors | PRODAFTUstaIoCUploadIndicators Syslog |
| Kind | Scheduled |
| Query frequency | 1h |
| Query period | 14d |
| Trigger threshold | 0 |
| Trigger operator | gt |
| Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/PRODAFT%20USTA%20-%20IoC%20Threat%20Intelligence/Analytic%20Rules/TIMapUrlToSyslog.yaml |
| Version | 1.0.0 |
| Arm template | 99ae4d06-d352-4926-a1d7-9c28e25b1313.json |
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
ThreatIntelIndicators
// Each USTA IoC feed uploads under its own SourceSystem ("PRODAFT USTA - Malicious URLs", etc.)
| where SourceSystem startswith "PRODAFT USTA"
// ObservableKey is a STIX kv pair ("url:value") - take the type before the colon
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
| where IndicatorType == "url"
| extend Url = ObservableValue
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
| project-reorder *, Tags, TrafficLightProtocolLevel, Url, Type
// innerunique: one match per indicator is enough to alert, and it keeps the join cheap
| join kind=innerunique (
Syslog
| where TimeGenerated >= ago(dt_lookBack)
// Pull the first URL out of the free-text message; rows without one drop out of the join
| extend Url = extract("(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)", 1,SyslogMessage)
| extend Syslog_TimeGenerated = TimeGenerated
) on Url
// non-expiring indicators are allowed above, so they must survive this filter too
| where isempty(ValidUntil) or Syslog_TimeGenerated < ValidUntil
| summarize Syslog_TimeGenerated = arg_max(Syslog_TimeGenerated , *) by Id, Url
| extend Description = tostring(parse_json(Data).description)
| extend IndicatorTags = tostring(parse_json(Data).labels)
| project timestamp = Syslog_TimeGenerated, Description, IndicatorTags, Id, Type, ValidUntil, Confidence, SyslogMessage, Computer, ProcessName, Url, HostIP
name: PRODAFT USTA - TI map URL to Syslog
triggerOperator: gt
query: |
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
ThreatIntelIndicators
// Each USTA IoC feed uploads under its own SourceSystem ("PRODAFT USTA - Malicious URLs", etc.)
| where SourceSystem startswith "PRODAFT USTA"
// ObservableKey is a STIX kv pair ("url:value") - take the type before the colon
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
| where IndicatorType == "url"
| extend Url = ObservableValue
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
| project-reorder *, Tags, TrafficLightProtocolLevel, Url, Type
// innerunique: one match per indicator is enough to alert, and it keeps the join cheap
| join kind=innerunique (
Syslog
| where TimeGenerated >= ago(dt_lookBack)
// Pull the first URL out of the free-text message; rows without one drop out of the join
| extend Url = extract("(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)", 1,SyslogMessage)
| extend Syslog_TimeGenerated = TimeGenerated
) on Url
// non-expiring indicators are allowed above, so they must survive this filter too
| where isempty(ValidUntil) or Syslog_TimeGenerated < ValidUntil
| summarize Syslog_TimeGenerated = arg_max(Syslog_TimeGenerated , *) by Id, Url
| extend Description = tostring(parse_json(Data).description)
| extend IndicatorTags = tostring(parse_json(Data).labels)
| project timestamp = Syslog_TimeGenerated, Description, IndicatorTags, Id, Type, ValidUntil, Confidence, SyslogMessage, Computer, ProcessName, Url, HostIP
queryFrequency: 1h
description: |
'Identifies a match in Syslog data from any malicious URL indicator ingested from PRODAFT
USTA IoC Threat Intelligence (SourceSystem starting with "PRODAFT USTA").'
id: 99ae4d06-d352-4926-a1d7-9c28e25b1313
triggerThreshold: 0
queryPeriod: 14d
version: 1.0.0
kind: Scheduled
relevantTechniques:
- T1071
severity: Medium
requiredDataConnectors:
- connectorId: Syslog
dataTypes:
- Syslog
- connectorId: PRODAFTUstaIoCUploadIndicators
dataTypes:
- ThreatIntelIndicators
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/PRODAFT%20USTA%20-%20IoC%20Threat%20Intelligence/Analytic%20Rules/TIMapUrlToSyslog.yaml
tactics:
- CommandAndControl
entityMappings:
- fieldMappings:
- identifier: HostName
columnName: Computer
entityType: Host
- fieldMappings:
- identifier: Address
columnName: HostIP
entityType: IP
- fieldMappings:
- identifier: Url
columnName: Url
entityType: URL
{
"$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/99ae4d06-d352-4926-a1d7-9c28e25b1313')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/99ae4d06-d352-4926-a1d7-9c28e25b1313')]",
"properties": {
"alertRuleTemplateName": "99ae4d06-d352-4926-a1d7-9c28e25b1313",
"customDetails": null,
"description": "'Identifies a match in Syslog data from any malicious URL indicator ingested from PRODAFT\nUSTA IoC Threat Intelligence (SourceSystem starting with \"PRODAFT USTA\").'\n",
"displayName": "PRODAFT USTA - TI map URL to Syslog",
"enabled": true,
"entityMappings": [
{
"entityType": "Host",
"fieldMappings": [
{
"columnName": "Computer",
"identifier": "HostName"
}
]
},
{
"entityType": "IP",
"fieldMappings": [
{
"columnName": "HostIP",
"identifier": "Address"
}
]
},
{
"entityType": "URL",
"fieldMappings": [
{
"columnName": "Url",
"identifier": "Url"
}
]
}
],
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/PRODAFT%20USTA%20-%20IoC%20Threat%20Intelligence/Analytic%20Rules/TIMapUrlToSyslog.yaml",
"query": "let dt_lookBack = 1h;\nlet ioc_lookBack = 14d;\nThreatIntelIndicators\n// Each USTA IoC feed uploads under its own SourceSystem (\"PRODAFT USTA - Malicious URLs\", etc.)\n| where SourceSystem startswith \"PRODAFT USTA\"\n// ObservableKey is a STIX kv pair (\"url:value\") - take the type before the colon\n| extend IndicatorType = replace(@\"\\[|\\]|\\\"\"\", \"\", tostring(split(ObservableKey, \":\", 0)))\n| where IndicatorType == \"url\"\n| extend Url = ObservableValue\n| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)\n| where TimeGenerated >= ago(ioc_lookBack)\n| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue\n| where IsActive and (ValidUntil > now() or isempty(ValidUntil))\n| project-reorder *, Tags, TrafficLightProtocolLevel, Url, Type\n// innerunique: one match per indicator is enough to alert, and it keeps the join cheap\n| join kind=innerunique (\n Syslog\n | where TimeGenerated >= ago(dt_lookBack)\n // Pull the first URL out of the free-text message; rows without one drop out of the join\n | extend Url = extract(\"(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\\\(\\\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)\", 1,SyslogMessage)\n | extend Syslog_TimeGenerated = TimeGenerated\n) on Url\n// non-expiring indicators are allowed above, so they must survive this filter too\n| where isempty(ValidUntil) or Syslog_TimeGenerated < ValidUntil\n| summarize Syslog_TimeGenerated = arg_max(Syslog_TimeGenerated , *) by Id, Url\n| extend Description = tostring(parse_json(Data).description)\n| extend IndicatorTags = tostring(parse_json(Data).labels)\n| project timestamp = Syslog_TimeGenerated, Description, IndicatorTags, Id, Type, ValidUntil, Confidence, SyslogMessage, Computer, ProcessName, Url, HostIP\n",
"queryFrequency": "PT1H",
"queryPeriod": "P14D",
"severity": "Medium",
"subTechniques": [],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"CommandAndControl"
],
"techniques": [
"T1071"
],
"templateVersion": "1.0.0",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}