Detection rules › Kusto
GCP Audit Logs - Open Firewall Rule Created or Modified
'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
| Tactic | Techniques |
|---|---|
| Initial Access | T1133 External Remote Services |
| Persistence | T1133 External Remote Services |
| Stealth | T1562.001 Impair Defenses: Disable or Modify Tools, T1562.004 Impair Defenses: Disable or Modify System Firewall |
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 =MethodName has "insert""Created"MethodName has "patch""Modified""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.
| Field | Kind | Values |
|---|---|---|
GCPResourceType | eq |
|
MethodName | match |
|
ServiceName | eq |
|
Severity | eq |
|
SourceRangeStr | in |
|
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.
| Field | Source |
|---|---|
AccountName | project |
AccountUPNSuffix | project |
AllowedPortsList | project |
AllowedProtocols | project |
AuthEmail | project |
CallerIpAddress | project |
Direction | project |
FirewallRuleName | project |
InsertId | project |
LogName | project |
MethodName | project |
Network | project |
OperationType | project |
PrincipalEmail | project |
Priority | project |
ProjectId | project |
ResourceName | project |
ServiceName | project |
Severity | project |
SourceRangesList | project |
TimeGenerated | project |
UserAgent | project |