Detection rules › Kusto

GCP Audit Logs - Open Firewall Rule Created or Modified

Status
available
Severity
high
Time window
1h
Group by
AuthEmail, CallerIpAddress, Direction, FirewallRuleName, GCPResourceName, InsertId, LogName, MethodName, Network, OperationType, PrincipalEmail, Priority, ProjectId, ServiceName, Severity, TimeGenerated, UserAgent
Source
github.com/Azure/Azure-Sentinel

'Detects when a Google Cloud Platform firewall rule is created or modified to allow traffic from any source (0.0.0.0/0 or 0.0.0.0). Open firewall rules expose resources to the internet and can significantly increase the attack surface of cloud infrastructure. This may indicate a misconfiguration, lack of security awareness, or malicious activity to create backdoor access. Adversaries may create or modify firewall rules to enable persistent access or facilitate lateral movement. This rule monitors firewall insert and patch operations where sourceRanges include unrestricted access patterns.'

MITRE ATT&CK coverage

Rules detecting the same action

Other rules on this platform that filter on the same API call or operation.

Rule body kusto

id: 8061c611-55f1-4ee5-a8f8-8f19f2c7aab2
name: GCP Audit Logs - Open Firewall Rule Created or Modified
description: |
  'Detects when a Google Cloud Platform firewall rule is created or modified to allow traffic from any source (0.0.0.0/0 or 0.0.0.0).
  Open firewall rules expose resources to the internet and can significantly increase the attack surface of cloud infrastructure.
  This may indicate a misconfiguration, lack of security awareness, or malicious activity to create backdoor access.
  Adversaries may create or modify firewall rules to enable persistent access or facilitate lateral movement.
  This rule monitors firewall insert and patch operations where sourceRanges include unrestricted access patterns.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: GCPAuditLogsDefinition
    dataTypes:
      - GCPAuditLogs
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
  - Persistence
  - InitialAccess
relevantTechniques:
  - T1562.004
  - T1133
  - T1562.001
tags:
  - GCP
  - Firewall
  - Network Security
  - Misconfiguration
query: |
  GCPAuditLogs
  | where ServiceName == "compute.googleapis.com"
  | where MethodName has_any ("firewalls.insert", "firewalls.patch")
  | where GCPResourceType == "gce_firewall_rule"
  | where Severity == "NOTICE"
  | extend 
      RequestJson = parse_json(Request),
      RequestMetadataJson = parse_json(RequestMetadata),
      AuthInfoJson = parse_json(AuthenticationInfo)
  | extend 
      SourceRanges = RequestJson.sourceRanges,
      Alloweds = RequestJson.alloweds,
      Direction = tostring(RequestJson.direction),
      RuleName = tostring(RequestJson.name),
      Network = tostring(RequestJson.network),
      Priority = tostring(RequestJson.priority),
      LogConfig = RequestJson.logConfig,
      Disabled = tobool(RequestJson.disabled)
  | mv-expand SourceRange = SourceRanges
  | extend SourceRangeStr = tostring(SourceRange)
  | where SourceRangeStr in ("0.0.0.0/0", "0.0.0.0")
  | extend 
      FirewallRuleName = extract(@"firewalls/([^/]+)$", 1, GCPResourceName),
      CallerIpAddress = tostring(RequestMetadataJson.callerIp),
      UserAgent = tostring(RequestMetadataJson.callerSuppliedUserAgent),
      AuthEmail = tostring(AuthInfoJson.principalEmail)
  | mv-expand Allowed = Alloweds
  | extend 
      AllowedProtocol = tostring(Allowed.IPProtocol),
      AllowedPorts = tostring(Allowed.ports),
      OperationType = case(
          MethodName has "insert", "Created",
          MethodName has "patch", "Modified",
          "Unknown")
  | summarize 
      AllowedProtocols = make_set(AllowedProtocol, 30),
      AllowedPortsList = make_set(AllowedPorts, 100),
      SourceRangesList = make_set(SourceRangeStr, 100)
      by TimeGenerated, PrincipalEmail, AuthEmail, ProjectId, FirewallRuleName, 
         GCPResourceName, Direction, Priority, Network, CallerIpAddress, UserAgent, 
         MethodName, ServiceName, Severity, OperationType, LogName, InsertId
  | extend
      AccountName = tostring(split(PrincipalEmail, "@")[0]), 
      AccountUPNSuffix = tostring(split(PrincipalEmail, "@")[1])
  | project TimeGenerated,
            PrincipalEmail,
            AuthEmail,
            ProjectId,
            FirewallRuleName,
            ResourceName = GCPResourceName,
            OperationType,
            Direction,
            SourceRangesList,
            AllowedProtocols,
            AllowedPortsList,
            Priority,
            Network,
            CallerIpAddress,
            UserAgent,
            MethodName,
            ServiceName,
            Severity,
            LogName,
            InsertId,
            AccountName,
            AccountUPNSuffix
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: PrincipalEmail
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: CallerIpAddress
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: ProjectId
      - identifier: InstanceName
        columnName: ResourceName
customDetails:
  ProjectId: ProjectId
  FirewallRuleName: FirewallRuleName
  ResourceName: ResourceName
  OperationType: OperationType
  Direction: Direction
  SourceRanges: SourceRangesList
  AllowedProtocols: AllowedProtocols
  AllowedPorts: AllowedPortsList
  Network: Network
alertDetailsOverride:
  alertDisplayNameFormat: "GCP Open Firewall Rule {{OperationType}} - {{FirewallRuleName}} by {{PrincipalEmail}}"
  alertDescriptionFormat: |-
    User {{PrincipalEmail}} created/modified firewall rule {{FirewallRuleName}} in project {{ProjectId}} to allow traffic from unrestricted sources (0.0.0.0/0).
    This configuration exposes resources to the internet and significantly increases attack surface. Investigate immediately to determine if this was authorized and assess potential security exposure. Consider restricting source ranges to specific trusted IP addresses or networks.
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

GCPAuditLogs

Stage 2: where

| where ServiceName == "compute.googleapis.com"

Stage 3: where

| where MethodName has_any ("firewalls.insert", "firewalls.patch")

Stage 4: where

| where GCPResourceType == "gce_firewall_rule"

Stage 5: where

| where Severity == "NOTICE"

Stage 6: extend

| extend 
    RequestJson = parse_json(Request),
    RequestMetadataJson = parse_json(RequestMetadata),
    AuthInfoJson = parse_json(AuthenticationInfo)

Stage 7: extend

| extend 
    SourceRanges = RequestJson.sourceRanges,
    Alloweds = RequestJson.alloweds,
    Direction = tostring(RequestJson.direction),
    RuleName = tostring(RequestJson.name),
    Network = tostring(RequestJson.network),
    Priority = tostring(RequestJson.priority),
    LogConfig = RequestJson.logConfig,
    Disabled = tobool(RequestJson.disabled)

Stage 8: mv-expand

| mv-expand SourceRange = SourceRanges

Stage 9: extend

| extend SourceRangeStr = tostring(SourceRange)

Stage 10: where

| where SourceRangeStr in ("0.0.0.0/0", "0.0.0.0")

Stage 11: extend

| extend 
    FirewallRuleName = extract(@"firewalls/([^/]+)$", 1, GCPResourceName),
    CallerIpAddress = tostring(RequestMetadataJson.callerIp),
    UserAgent = tostring(RequestMetadataJson.callerSuppliedUserAgent),
    AuthEmail = tostring(AuthInfoJson.principalEmail)

Stage 12: mv-expand

| mv-expand Allowed = Alloweds

Stage 13: extend

| extend 
    AllowedProtocol = tostring(Allowed.IPProtocol),
    AllowedPorts = tostring(Allowed.ports),
    OperationType = case(
        MethodName has "insert", "Created",
        MethodName has "patch", "Modified",
        "Unknown")
OperationType =
ifMethodName has "insert""Created"
elifMethodName has "patch""Modified"
else"Unknown"

Stage 14: summarize

| summarize 
    AllowedProtocols = make_set(AllowedProtocol, 30),
    AllowedPortsList = make_set(AllowedPorts, 100),
    SourceRangesList = make_set(SourceRangeStr, 100)
    by TimeGenerated, PrincipalEmail, AuthEmail, ProjectId, FirewallRuleName, 
       GCPResourceName, Direction, Priority, Network, CallerIpAddress, UserAgent, 
       MethodName, ServiceName, Severity, OperationType, LogName, InsertId

Stage 15: extend

| extend
    AccountName = tostring(split(PrincipalEmail, "@")[0]), 
    AccountUPNSuffix = tostring(split(PrincipalEmail, "@")[1])

Stage 16: project

| project TimeGenerated,
          PrincipalEmail,
          AuthEmail,
          ProjectId,
          FirewallRuleName,
          ResourceName = GCPResourceName,
          OperationType,
          Direction,
          SourceRangesList,
          AllowedProtocols,
          AllowedPortsList,
          Priority,
          Network,
          CallerIpAddress,
          UserAgent,
          MethodName,
          ServiceName,
          Severity,
          LogName,
          InsertId,
          AccountName,
          AccountUPNSuffix

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
GCPResourceTypeeq
  • gce_firewall_rule transforms: cased
MethodNamematch
  • firewalls.insert
  • firewalls.patch
ServiceNameeq
  • compute.googleapis.com transforms: cased
Severityeq
  • NOTICE transforms: cased
SourceRangeStrin
  • 0.0.0.0 transforms: cased
  • 0.0.0.0/0 transforms: cased

Output fields

Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.

FieldSource
AccountNameproject
AccountUPNSuffixproject
AllowedPortsListproject
AllowedProtocolsproject
AuthEmailproject
CallerIpAddressproject
Directionproject
FirewallRuleNameproject
InsertIdproject
LogNameproject
MethodNameproject
Networkproject
OperationTypeproject
PrincipalEmailproject
Priorityproject
ProjectIdproject
ResourceNameproject
ServiceNameproject
Severityproject
SourceRangesListproject
TimeGeneratedproject
UserAgentproject