AnalysisType: rule
Filename: azure_automation_webhook_created.py
RuleID: "Azure.MonitorActivity.Automation.WebhookCreated"
DisplayName: "Azure Automation Webhook Created"
Enabled: true
LogTypes:
- Azure.MonitorActivity
Severity: Info
Description: >
Detects when an Azure Automation webhook is created. A webhook uses a custom URL passed to
Azure Automation along with a data payload specific to the runbook. Adversaries may exploit
this capability to trigger runbooks containing malicious code for persistence or to execute
unauthorized actions in the environment.
Reports:
MITRE ATT&CK:
- TA0003:T1546 # Persistence: Event Triggered Execution
- TA0042:T1608 # Resource Development: Stage Capabilities
Tags:
- AZT502
- AZT503
- AZT503.3
- Persistence
- Resource Development
- Event Triggered Execution
- Stage Capabilities
Runbook: |
1. Query Azure Monitor Activity logs for all automation account operations (account creation, runbook creation, webhook creation) by the callerIpAddress in the 24 hours before and after the alert to identify a sequence of persistence activities
2. Find all webhook creations in the past 6 hours to determine if multiple persistence mechanisms are being established
3. Check if the callerIpAddress has created webhooks in the past 90 days to determine if this is typical automation workflow deployment
Reference: https://github.com/elastic/detection-rules/blob/main/rules/integrations/azure/persistence_automation_webhook_created.toml
SummaryAttributes:
- resourceId
- callerIpAddress
- correlationId
Tests:
- Name: Automation Webhook Created with ACTION
ExpectedResult: true
Log:
{
"time": "2025-12-22T10:30:00.0000000Z",
"resourceId": "/subscriptions/12345678-1234-1234-1234-123456789abc/resourceGroups/automation-rg/providers/Microsoft.Automation/automationAccounts/my-automation/webhooks/malicious-webhook",
"operationName": "MICROSOFT.AUTOMATION/AUTOMATIONACCOUNTS/WEBHOOKS/ACTION",
"operationVersion": "2021-06-22",
"category": "Administrative",
"resultType": "Success",
"callerIpAddress": "1.1.1.1",
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"level": "Informational",
"location": "eastus",
"tenantId": "87654321-4321-4321-4321-111111111111"
}
- Name: Automation Webhook Created with WRITE
ExpectedResult: true
Log:
{
"time": "2025-12-22T11:15:00.0000000Z",
"resourceId": "/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/prod-automation/providers/Microsoft.Automation/automationAccounts/prod-automation/webhooks/prod-webhook",
"operationName": "MICROSOFT.AUTOMATION/AUTOMATIONACCOUNTS/WEBHOOKS/WRITE",
"category": "Administrative",
"resultType": "Succeeded",
"callerIpAddress": "2.2.2.2",
"correlationId": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
"level": "Information",
"location": "westeurope",
"tenantId": "22222222-2222-2222-2222-222222222222"
}
- Name: Case Insensitive Match
ExpectedResult: true
Log:
{
"time": "2025-12-22T12:00:00.0000000Z",
"resourceId": "/subscriptions/33333333-3333-3333-3333-333333333333/resourceGroups/test-rg/providers/Microsoft.Automation/automationAccounts/test-account/webhooks/test-webhook",
"operationName": "microsoft.automation/automationaccounts/webhooks/action",
"category": "Administrative",
"resultType": "Success",
"callerIpAddress": "3.3.3.3",
"correlationId": "c3d4e5f6-a7b8-9012-cdef-333333333333",
"location": "centralus",
"tenantId": "33333333-3333-3333-3333-333333333333"
}
- Name: Different Operation
ExpectedResult: false
Log:
{
"time": "2025-12-22T14:00:00.0000000Z",
"resourceId": "/subscriptions/55555555-5555-5555-5555-555555555555/resourceGroups/automation-rg/providers/Microsoft.Automation/automationAccounts/my-automation/webhooks/old-webhook",
"operationName": "MICROSOFT.AUTOMATION/AUTOMATIONACCOUNTS/WEBHOOKS/DELETE",
"category": "Administrative",
"resultType": "Success",
"callerIpAddress": "5.5.5.5",
"correlationId": "e5f6a7b8-c9d0-1234-ef01-567890123456",
"location": "southcentralus",
"tenantId": "55555555-5555-5555-5555-555555555555"
}
# ------ paired body: azure_automation_webhook_created.py ------
from panther_azureactivity_helpers import (
azure_activity_alert_context,
azure_activity_success,
extract_resource_name_from_id,
)
AUTOMATION_WEBHOOK_OPERATIONS = [
"MICROSOFT.AUTOMATION/AUTOMATIONACCOUNTS/WEBHOOKS/ACTION",
"MICROSOFT.AUTOMATION/AUTOMATIONACCOUNTS/WEBHOOKS/WRITE",
]
def rule(event):
return event.get(
"operationName", ""
).upper() in AUTOMATION_WEBHOOK_OPERATIONS and azure_activity_success(event)
def title(event):
resource_id = event.get("resourceId", "<UNKNOWN_RESOURCE>")
webhook_name = extract_resource_name_from_id(
resource_id, "webhooks", default="<UNKNOWN_WEBHOOK_NAME>"
)
return f"Azure Automation Webhook Created: [{webhook_name}]"
def alert_context(event):
context = azure_activity_alert_context(event)
resource_id = event.get("resourceId", "")
webhook_name = extract_resource_name_from_id(resource_id, "webhooks", default="")
if webhook_name:
context["webhook_name"] = webhook_name
automation_account_name = extract_resource_name_from_id(
resource_id, "automationAccounts", default=""
)
if automation_account_name:
context["automation_account_name"] = automation_account_name
return context