Hey,
I'm writing a detection rule which I'm struggling with the syntax and not sure about the variable section i put in.
intro:
The rule is suppose to combine 2 events:
Event 1: is to detect when a security group was open to any-any rule (0.0.0.0) - which we have a working rule on that already.
Event 2: detect when this security group is closed to 0.0.0.0 comunication - we have a lambda function that closes this automatically.
So basically, we want to monitor if the lambda is not working - then pop up a detection alert.
but when working on the the rule, I come acrros this error which is related to the placeholder:
parsing: error with token: ":" invalid operator in events predicate line: 15
This is the rule:
rule ttp_aws_security_group_open_to_world {
meta:
rule_name = "AWS Security Group Open to World"
description = "Detects when an AWS Security Group is open to the world (0.0.0.0/0 or ::/0) and then revoked within 5 minutes."
severity = "High"
tactic = "TA0005" // Defense Evasion
technique = "T1562.001" // Impair Process Control: Network Firewall
platform = "AWS"
references = "https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_AuthorizeSecurityGroupIngress.html, https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_RevokeSecurityGroupIngress.html"
tags = "cloud, aws, security_group, misconfiguration, ephemeral, network_exposure, cloudtrail"
events:
// Event 1: Detect when a security group ingress rule is opened to the world
// This is the first event variable
$open_event:
$open_event.metadata.vendor_name = "AMAZON"
$open_event.metadata.product_name = "AWS CloudTrail"
$open_event.metadata.product_event_type = "AuthorizeSecurityGroupIngress"
// Corrected: Check for 'any-any' IPv4 or IPv6 CIDR blocks using standard UDM field
(
$open_event.network.vpc.firewall.rule.source_ip_addresses = "0.0.0.0/0" or
$open_event.network.vpc.firewall.rule.source_ip_addresses = "::/0"
)
// Note: security_result.action is not typically populated for API calls like AuthorizeSecurityGroupIngress
// and is removed to prevent false negatives.
// Capture key fields for precise correlation and outcome:
$open_event.target.resource.resource_id // The Security Group ID (e.g., sg-0abcdef1234567890)
$open_event.network.vpc.firewall.rule.ip_protocol // The protocol (e.g., "tcp", "udp", "-1")
$open_event.network.vpc.firewall.rule.target_port // The target port or range
$open_event.network.vpc.firewall.rule.source_ip_addresses // The specific CIDR (0.0.0.0/0 or ::/0)
$open_event.additional.fields["recipientAccountId"] // Account ID
// Event 2: Detect when the same security group ingress rule is revoked
// This is the second event variable
$close_event:
$close_event.metadata.vendor_name = "AMAZON"
$close_event.metadata.product_name = "AWS CloudTrail"
$close_event.metadata.product_event_type = "RevokeSecurityGroupIngress"
// Corrected: Check for the revocation of 'any-any' IPv4 or IPv6 CIDR blocks
(
$close_event.network.vpc.firewall.rule.source_ip_addresses = "0.0.0.0/0" or
$close_event.network.vpc.firewall.rule.source_ip_addresses = "::/0"
)
// Note: security_result.action is not typically populated for API calls like RevokeSecurityGroupIngress
// and is removed.
// Capture the same correlation fields as the $open_event:
$close_event.target.resource.resource_id
$close_event.network.vpc.firewall.rule.ip_protocol
$close_event.network.vpc.firewall.rule.target_port
$close_event.network.vpc.firewall.rule.source_ip_addresses
$close_event.additional.fields["recipientAccountId"]
condition:
// Both events must occur for the rule to trigger.
$open_event and $close_event and
// Crucial correlation: Ensure both events refer to the *exact same* ingress rule.
// We correlate on the Security Group ID, the protocol, the target port, and the specific CIDR.
$open_event.target.resource.resource_id = $close_event.target.resource.resource_id and
$open_event.network.vpc.firewall.rule.ip_protocol = $close_event.network.vpc.firewall.rule.ip_protocol and
$open_event.network.vpc.firewall.rule.target_port = $close_event.network.vpc.firewall.rule.target_port and
// Ensure the same "any-any" CIDR was opened and then revoked (handles both IPv4 and IPv6)
(
($open_event.network.vpc.firewall.rule.source_ip_addresses = "0.0.0.0/0" and $close_event.network.vpc.firewall.rule.source_ip_addresses = "0.0.0.0/0") or
($open_event.network.vpc.firewall.rule.source_ip_addresses = "::/0" and $close_event.network.vpc.firewall.rule.source_ip_addresses = "::/0")
) and
// Time window: The $close_event must occur within 5 minutes (300 seconds) of the $open_event.
// The `by` clause ensures that the correlation is performed per unique combination of the specified fields.
$open_event and $close_event by
$open_event.target.resource.resource_id,
$open_event.network.vpc.firewall.rule.ip_protocol,
$open_event.network.vpc.firewall.rule.target_port,
$open_event.network.vpc.firewall.rule.source_ip_addresses
match_within 5m on $open_event.metadata.event_timestamp.seconds
outcome:
$risk_score = max(35)
$vendor_name = "AMAZON"
$product_name = "AWS CloudTrail"
// Count distinct IDs from both open and close events
$event_count = count_distinct($open_event.metadata.id, $close_event.metadata.id)
// Collect distinct user agents from both events
$user_agents = array_distinct($open_event.network.http.user_agent, $close_event.network.http.user_agent)
// Collect distinct network organization names from both events
$network_org_names = array_distinct($open_event.principal.ip_geo_artifact.network.organization_name, $close_event.principal.ip_geo_artifact.network.organization_name)
// Collect distinct principal IPs from both events
$principal_ips = array_distinct($open_event.principal.ip, $close_event.principal.ip)
// Collect distinct recipient account IDs from both events
$recipient_account_ids = array_distinct($open_event.additional.fields["recipientAccountId"], $close_event.additional.fields["recipientAccountId"])
// Collect distinct principal users from both events
$principal_users = array_distinct($open_event.principal.user.userid, $close_event.principal.user.userid)
// Collect distinct cloud regions from both events
$cloud_regions = array_distinct($open_event.principal.location.name, $close_event.principal.location.name)
// Collect distinct target resource IDs (Security Group IDs) from both events
$target_resource_ids = array_distinct($open_event.target.resource.resource_id, $close_event.target.resource.resource_id)
// Specific details about the opened rule:
$opened_ports = array_distinct($open_event.network.vpc.firewall.rule.target_port)
$opened_protocols = array_distinct($open_event.network.vpc.firewall.rule.ip_protocol)
$opened_cidrs = array_distinct($open_event.network.vpc.firewall.rule.source_ip_addresses)
}
Would appreciate any advice .
Thanks