Azure Resource Lock Deleted


Description

Detects when Azure resource locks are deleted. Storm-0501 and other ransomware operators delete resource locks before destroying storage accounts and backups, as locks prevent deletion even by administrators. This is a critical pre-ransomware indicator.

Query · python

from panther_azureactivity_helpers import (
    azure_activity_alert_context,
    azure_activity_success,
    extract_resource_name_from_id,
)

LOCK_DELETE = "MICROSOFT.AUTHORIZATION/LOCKS/DELETE"


def rule(event):
    return event.get("operationName", "").upper() == LOCK_DELETE and azure_activity_success(event)


def title(event):
    resource_id = event.get("resourceId", "")
    lock_name = extract_resource_name_from_id(resource_id, "locks", default="<UNKNOWN_LOCK>")

    return f"Azure resource lock [{lock_name}] deleted"


def alert_context(event):
    context = azure_activity_alert_context(event)

    resource_id = event.get("resourceId", "")

    lock_name = extract_resource_name_from_id(resource_id, "locks", default="")
    if lock_name:
        context["lock_name"] = lock_name

    # Extract the resource that was protected by the lock
    resource_group = extract_resource_name_from_id(resource_id, "resourceGroups", default="")
    if resource_group:
        context["resource_group"] = resource_group

    # Check if this is a subscription-level or resource-level lock
    if "/subscriptions/" in resource_id and "/resourceGroups/" not in resource_id:
        context["lock_scope"] = "subscription"
    elif "/resourceGroups/" in resource_id:
        context["lock_scope"] = "resource_group_or_resource"

    return context

Analyst notes

  1. Query Azure Monitor Activity logs for all lock deletions by the callerIpAddress and caller identity in the 24 hours before and after the alert to calculate the total number of locks removed
  2. Search for subsequent destructive operations (storage account deletions, snapshot deletions, immutability policy deletions, blob deletions) by the same caller in the 6 hours after lock deletion to identify ransomware attack pattern
  3. Check if the callerIpAddress or caller identity has performed lock deletions in the past 90 days to establish if this is routine maintenance or anomalous activity
  4. Review Azure.Audit logs for authentication events from the callerIpAddress in the 48 hours before the lock deletion to identify signs of credential compromise (unusual locations, MFA changes, privilege escalations)
  5. Search for other alerts triggered by the same callerIpAddress or caller identity in the past 7 days to assess if this is part of a broader attack campaign
Raw source Azure Resource Lock Deleted · Panther Python
Esc
Published by panther-labs/panther-analysis ↗, licensed under Apache 2.0 ↗. Reproduced here unmodified.
AnalysisType: rule
Filename: azure_resource_lock_deleted.py
RuleID: "Azure.MonitorActivity.Authorization.ResourceLockDeleted"
DisplayName: "Azure Resource Lock Deleted"
Enabled: true
LogTypes:
  - Azure.MonitorActivity
Severity: High
Description: >
  Detects when Azure resource locks are deleted. Storm-0501 and other ransomware operators
  delete resource locks before destroying storage accounts and backups, as locks prevent
  deletion even by administrators. This is a critical pre-ransomware indicator.
Reports:
  MITRE ATT&CK:
    - TA0005:T1562 # Defense Evasion: Impair Defenses
    - TA0005:T1562.001 # Defense Evasion: Disable or Modify Tools
    - TA0040:T1490 # Impact: Inhibit System Recovery
Reference: https://www.microsoft.com/en-us/security/blog/2025/08/27/storm-0501s-evolving-techniques-lead-to-cloud-based-ransomware/
Tags:
  - Defense Evasion
  - Impair Defenses
  - Inhibit System Recovery
  - Ransomware
  - Storm-0501
Runbook: |
  1. Query Azure Monitor Activity logs for all lock deletions by the callerIpAddress and caller identity in the 24 hours before and after the alert to calculate the total number of locks removed
  2. Search for subsequent destructive operations (storage account deletions, snapshot deletions, immutability policy deletions, blob deletions) by the same caller in the 6 hours after lock deletion to identify ransomware attack pattern
  3. Check if the callerIpAddress or caller identity has performed lock deletions in the past 90 days to establish if this is routine maintenance or anomalous activity
  4. Review Azure.Audit logs for authentication events from the callerIpAddress in the 48 hours before the lock deletion to identify signs of credential compromise (unusual locations, MFA changes, privilege escalations)
  5. Search for other alerts triggered by the same callerIpAddress or caller identity in the past 7 days to assess if this is part of a broader attack campaign
SummaryAttributes:
  - resourceId
  - callerIpAddress
  - correlationId
Tests:
  - Name: Resource Lock Deleted Successfully
    ExpectedResult: true
    Log:
      {
        "time": "2025-01-27T14:23:00.0000000Z",
        "resourceId": "/subscriptions/12345678-1234-1234-1234-123456789abc/resourceGroups/critical-data-rg/providers/Microsoft.Storage/storageAccounts/criticaldata001/providers/Microsoft.Authorization/locks/DoNotDelete",
        "operationName": "MICROSOFT.AUTHORIZATION/LOCKS/DELETE",
        "operationVersion": "2017-04-01",
        "category": "Administrative",
        "resultType": "Success",
        "resultSignature": "200",
        "callerIpAddress": "203.0.113.42",
        "correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "level": "Informational",
        "location": "eastus",
        "tenantId": "87654321-4321-4321-4321-111111111111",
        "identity": {
          "claims": {
            "name": "compromised-admin@company.com",
            "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn": "compromised-admin@company.com"
          }
        }
      }
  - Name: Subscription Level Lock Deleted
    ExpectedResult: true
    Log:
      {
        "time": "2025-01-27T15:45:00.0000000Z",
        "resourceId": "/subscriptions/87654321-4321-4321-4321-987654321cba/providers/Microsoft.Authorization/locks/SubscriptionDoNotDelete",
        "operationName": "microsoft.authorization/locks/delete",
        "operationVersion": "2017-04-01",
        "category": "Administrative",
        "resultType": "Succeeded",
        "callerIpAddress": "198.51.100.50",
        "correlationId": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
        "level": "Information",
        "location": "global",
        "tenantId": "11111111-1111-1111-1111-111111111111"
      }
  - Name: Resource Group Lock Deleted
    ExpectedResult: true
    Log:
      {
        "time": "2025-01-27T16:10:00.0000000Z",
        "resourceId": "/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/production-rg/providers/Microsoft.Authorization/locks/ReadOnlyLock",
        "operationName": "Microsoft.Authorization/locks/delete",
        "category": "Administrative",
        "resultType": "Success",
        "callerIpAddress": "192.0.2.100",
        "correlationId": "c3d4e5f6-a7b8-9012-cdef-345678901234",
        "tenantId": "22222222-2222-2222-2222-222222222222"
      }
  - Name: Failed Lock Deletion
    ExpectedResult: false
    Log:
      {
        "time": "2025-01-27T17:00:00.0000000Z",
        "resourceId": "/subscriptions/12345678-1234-1234-1234-123456789abc/resourceGroups/protected-rg/providers/Microsoft.Authorization/locks/CriticalLock",
        "operationName": "MICROSOFT.AUTHORIZATION/LOCKS/DELETE",
        "category": "Administrative",
        "resultType": "Failed",
        "resultSignature": "403",
        "callerIpAddress": "198.18.0.50",
        "correlationId": "d4e5f6a7-b8c9-0123-def0-456789012345",
        "tenantId": "33333333-3333-3333-3333-333333333333"
      }
  - Name: Different Operation - Lock Created
    ExpectedResult: false
    Log:
      {
        "time": "2025-01-27T18:00:00.0000000Z",
        "resourceId": "/subscriptions/12345678-1234-1234-1234-123456789abc/resourceGroups/data-rg/providers/Microsoft.Authorization/locks/NewLock",
        "operationName": "MICROSOFT.AUTHORIZATION/LOCKS/WRITE",
        "category": "Administrative",
        "resultType": "Success",
        "callerIpAddress": "203.0.113.10",
        "correlationId": "e5f6a7b8-c9d0-1234-ef01-567890123456",
        "tenantId": "44444444-4444-4444-4444-444444444444"
      }


# ------ paired body: azure_resource_lock_deleted.py ------

from panther_azureactivity_helpers import (
    azure_activity_alert_context,
    azure_activity_success,
    extract_resource_name_from_id,
)

LOCK_DELETE = "MICROSOFT.AUTHORIZATION/LOCKS/DELETE"


def rule(event):
    return event.get("operationName", "").upper() == LOCK_DELETE and azure_activity_success(event)


def title(event):
    resource_id = event.get("resourceId", "")
    lock_name = extract_resource_name_from_id(resource_id, "locks", default="<UNKNOWN_LOCK>")

    return f"Azure resource lock [{lock_name}] deleted"


def alert_context(event):
    context = azure_activity_alert_context(event)

    resource_id = event.get("resourceId", "")

    lock_name = extract_resource_name_from_id(resource_id, "locks", default="")
    if lock_name:
        context["lock_name"] = lock_name

    # Extract the resource that was protected by the lock
    resource_group = extract_resource_name_from_id(resource_id, "resourceGroups", default="")
    if resource_group:
        context["resource_group"] = resource_group

    # Check if this is a subscription-level or resource-level lock
    if "/subscriptions/" in resource_id and "/resourceGroups/" not in resource_id:
        context["lock_scope"] = "subscription"
    elif "/resourceGroups/" in resource_id:
        context["lock_scope"] = "resource_group_or_resource"

    return context

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.