Deploying Sentinel hunting queries using Terraform

Ok, so creating hunting queries in Sentinel using Terraform works fine, but it is very funky to actually understand, even though it works. First of all, you need to use the log_analytics_saved_search resource, nothing more. However, for the keen observer, this resource does not support entity mapping and no MITRE ATT&CK. Or does it? 😉

Let’s say we save a hunting query in the GUI, with both entity mappings and Tactics and Techniques:

What will actually happen on the API side is interesting:

So, the entity mapping is actually added on the end of the query, while the tactics and techniques are tags!

Let’s first try some Terraform code without tags and the end of the query for entity mapping:

resource "azurerm_log_analytics_saved_search" "example" {
  name                       = "MARIUS - AV - Extension exluded from WD-AV"
  log_analytics_workspace_id = "/subscriptions/9889941a-7c00-4cf1-972e-ec150e322282/resourceGroups/sentinel/providers/Microsoft.OperationalInsights/workspaces/42sentineldev"

  category     = "Hunting Queries"
  display_name = "MARIUS - AV - Extension exluded from WD-AV"
  query        = <<QUERY
    // Use this rule to determine if an extension was excluded from Windows Defender AV 
    // (https://m365internals.com/2021/07/05/why-are-windows-defender-av-logs-so-important-and-how-to-monitor-them-with-azure-sentinel/)
    let timeframe = 7d; 
    Event 
    | where TimeGenerated >= ago(timeframe) 
    | where EventLog == "Microsoft-Windows-Windows Defender/Operational" 
    | parse EventData with * 'New Value">'RegistryKey'</Data>' * 
    | where RegistryKey startswith "HKLM\\SOFTWARE\\Microsoft\\Windows Defender\\Exclusions\\Extensions" 
    | extend ExcludedExtension = tostring(split(RegistryKey, "Extensions\\\\")[1]) 
    | extend ExcludedExtension = tostring(split(ExcludedExtension, "=")[0]) 
    | where ExcludedExtension endswith ".ps1" or ExcludedExtension endswith ".vbs" or ExcludedExtension endswith ".bat" 
    | project TimeGenerated, Computer, ExcludedExtension, RegistryKey
QUERY
}

As you can see, this has no techniques and no entity mappings:

However, now we add tags and entity mapping to our query:

resource "azurerm_log_analytics_saved_search" "example" {
  name                       = "MARIUS - AV - Extension exluded from WD-AV"
  log_analytics_workspace_id = "/subscriptions/9889941a-7c00-4cf1-972e-ec150e322282/resourceGroups/sentinel/providers/Microsoft.OperationalInsights/workspaces/42sentineldev"

  category     = "Hunting Queries"
  display_name = "MARIUS - AV - Extension exluded from WD-AV"
  query        = <<QUERY
    // Use this rule to determine if an extension was excluded from Windows Defender AV 
    // (https://m365internals.com/2021/07/05/why-are-windows-defender-av-logs-so-important-and-how-to-monitor-them-with-azure-sentinel/)
    let timeframe = 7d; 
    Event 
    | where TimeGenerated >= ago(timeframe) 
    | where EventLog == "Microsoft-Windows-Windows Defender/Operational" 
    | parse EventData with * 'New Value">'RegistryKey'</Data>' * 
    | where RegistryKey startswith "HKLM\\SOFTWARE\\Microsoft\\Windows Defender\\Exclusions\\Extensions" 
    | extend ExcludedExtension = tostring(split(RegistryKey, "Extensions\\\\")[1]) 
    | extend ExcludedExtension = tostring(split(ExcludedExtension, "=")[0]) 
    | where ExcludedExtension endswith ".ps1" or ExcludedExtension endswith ".vbs" or ExcludedExtension endswith ".bat" 
    | project TimeGenerated, Computer, ExcludedExtension, RegistryKey
    | extend IP_0_Address = Computer
QUERY

    tags = {
        tactics: "CredentialAccess,DefenseEvasion"
        techniques: "T1134,T1134.002"
    }
}

And that’s it, we have both entity mapping, tactics and techniques set for our hunting queries!

Good luck!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s