aws-waf-swap-web-acl

Replace a Web ACL with an attacker-controlled permissive one to weaken HTTP-layer filtering (requires owning a substitute Web ACL).

derived aws emits CanModify

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} HasPermission {'webacl': None}

where

node_type(?webacl) == SecurityService ?webacl.provider_type == 'AWS::WAFv2::WebACL' effective_action_on(?principal, ?webacl) contains 'wafv2:AssociateWebACL'

emit

source typeIdentity
target typeSecurityService
source?principal
target?webacl
permissionswafv2:AssociateWebACL
conditionsiam_permission scp_or_org_policy service_state
state logicCONDITIONAL(service_state) — AssociateWebACL is only an attack primitive if the principal also controls a permissive Web ACL to substitute (either by holding wafv2:CreateWebACL to create a new one, or by already owning another Web ACL in the same scope/region). ACTIVE only if evaluator confirms the principal can provide a substitute Web ACL. BLOCKED if an SCP or permission boundary denies wafv2:AssociateWebACL.

Narrative

{principal.name} can associate a Web ACL (wafv2:AssociateWebACL). If {principal.name} also controls a permissive Web ACL, this can replace the current protective one, weakening or removing HTTP-layer filtering from protected resources. This reduces detection coverage and may allow previously-blocked traffic - it is a cover-tracks / weaken-defenses action, not an access grant.

Raw rule rules/derived/aws/waf.yaml

id: aws-waf-swap-web-acl
emits: CanModify
description: Replace a Web ACL with an attacker-controlled permissive one to weaken HTTP-layer filtering
  (requires owning a substitute Web ACL).
applies_to:
- aws
match:
- - principal: null
  - HasPermission
  - webacl: null
where:
- node_type(?webacl) == SecurityService
- ?webacl.provider_type == 'AWS::WAFv2::WebACL'
- effective_action_on(?principal, ?webacl) contains 'wafv2:AssociateWebACL'
emit:
  source_type: Identity
  target_type: SecurityService
  source: ?principal
  target: ?webacl
  permissions:
  - wafv2:AssociateWebACL
  conditions:
  - iam_permission
  - scp_or_org_policy
  - service_state
  state_logic: "CONDITIONAL(service_state) \u2014 AssociateWebACL is only an attack primitive if the principal\
    \ also controls a permissive Web ACL to substitute (either by holding wafv2:CreateWebACL to create\
    \ a new one, or by already owning another Web ACL in the same scope/region). ACTIVE only if evaluator\
    \ confirms the principal can provide a substitute Web ACL. BLOCKED if an SCP or permission boundary\
    \ denies wafv2:AssociateWebACL."
  confidence: 0.85
  derived_from:
  - ?principal HasPermission ?webacl (wafv2:AssociateWebACL)
  false_positive_note: "AssociateWebACL without a substitute ACL is a no-op. Do NOT emit ACTIVE unless\
    \ the principal demonstrably controls another Web ACL in the same scope/region or holds wafv2:CreateWebACL.\
    \ This edge is a DEFENSE-EVASION primitive \u2014 it does NOT grant access to the application or data\
    \ protected by the Web ACL. Do NOT chain this edge to CanEnterAccount / CanExecuteAs / CanEscalateTo.\
    \ Scope to the correct AWS region (CLOUDFRONT scope is us-east-1 only)."
  narrative: "{principal.name} can associate a Web ACL (wafv2:AssociateWebACL). If {principal.name} also\
    \ controls a permissive Web ACL, this can replace the current protective one, weakening or removing\
    \ HTTP-layer filtering from protected resources. This reduces detection coverage and may allow previously-blocked\
    \ traffic \u2014 it is a cover-tracks / weaken-defenses action, not an access grant."
move · open · esc close