aws-appflow-create-flow-exfil
Creating a new AppFlow flow that references an existing connector profile and points to an attacker-controlled destination exfiltrates SaaS data without modifying any existing flow.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
?principal has effective appflow:CreateFlow (resource scope: *)
?principal has effective appflow:UseConnectorProfile on at least one connector profile with SaaS credentials
?principal can also trigger the new flow: appflow:StartFlow on the newly created flow ARN, OR the new flow is configured with a schedule/event trigger
emit
| source type | Identity |
|---|---|
| target type | Data |
| source | ?principal |
| target | <data accessible via the referenced connector profile's SaaS connection> |
| permissions | appflow:CreateFlow appflow:UseConnectorProfile appflow:StartFlow? |
| conditions | iam_permission resource_policy |
| state logic | CONDITIONAL(resource_policy) unless the attacker-controlled destination S3 bucket already has an appflow.amazonaws.com write grant — without that grant the flow run fails at the S3 write step. Upgrade toward ACTIVE when the principal also controls the destination bucket policy (s3:PutBucketPolicy on the target bucket). Also CONDITIONAL(trigger_exists) if the principal cannot call appflow:StartFlow on the newly created flow — but a flow created with a schedule trigger fires automatically, so for scheduled new flows this condition is met at creation time. Inherit weakest from contributing CanCreate. |
Narrative
{principal.name} can create a new AppFlow flow (appflow:CreateFlow) referencing an existing connector profile (appflow:UseConnectorProfile), configuring the destination as an attacker-controlled S3 bucket; when started or scheduled, the new flow exfiltrates SaaS data from the referenced connector without modifying any existing flow.
Raw rule rules/derived/aws/appflow.yaml
id: aws-appflow-create-flow-exfil
emits: CanExfiltrate
description: Creating a new AppFlow flow that references an existing connector profile and points to an
attacker-controlled destination exfiltrates SaaS data without modifying any existing flow.
applies_to:
- aws
match:
- - principal: null
- CanCreate
- scope: null
where:
- '?principal has effective appflow:CreateFlow (resource scope: *)'
- ?principal has effective appflow:UseConnectorProfile on at least one connector profile with SaaS credentials
- '?principal can also trigger the new flow: appflow:StartFlow on the newly created flow ARN, OR the new
flow is configured with a schedule/event trigger'
emit:
source_type: Identity
target_type: Data
source: ?principal
target: <data accessible via the referenced connector profile's SaaS connection>
permissions:
- appflow:CreateFlow
- appflow:UseConnectorProfile
- appflow:StartFlow?
conditions:
- iam_permission
- resource_policy
state_logic: "CONDITIONAL(resource_policy) unless the attacker-controlled destination S3 bucket already\
\ has an appflow.amazonaws.com write grant \u2014 without that grant the flow run fails at the S3\
\ write step. Upgrade toward ACTIVE when the principal also controls the destination bucket policy\
\ (s3:PutBucketPolicy on the target bucket). Also CONDITIONAL(trigger_exists) if the principal cannot\
\ call appflow:StartFlow on the newly created flow \u2014 but a flow created with a schedule trigger\
\ fires automatically, so for scheduled new flows this condition is met at creation time. Inherit\
\ weakest from contributing CanCreate."
confidence: min(contributing_confidences) * 0.80
derived_from:
- ?principal CanCreate ?scope (appflow:CreateFlow + appflow:UseConnectorProfile)
- 'messaging-chains.yaml (cited: data-plane exfil via new messaging path creation)'
false_positive_note: "appflow:UseConnectorProfile is a REQUIRED gate \u2014 confirm the principal holds\
\ it on at least one connector profile ARN before emitting. Without it, CreateFlow rejects any flow\
\ that references a connector profile (which is every meaningful exfil flow). Also: CreateFlow requires\
\ Resource:* in the IAM policy (no ARN pre-exists); honor that: a policy with appflow:CreateFlow scoped\
\ to a specific flow ARN does NOT satisfy CreateFlow. Scope the CanCreate emit to the account/ region\
\ scope, not a specific flow node. The destination bucket policy gate is the same as in aws-appflow-update-flow-exfil:\
\ emit CONDITIONAL(resource_policy) when unconfirmed."
narrative: '{principal.name} can create a new AppFlow flow (appflow:CreateFlow) referencing an existing
connector profile (appflow:UseConnectorProfile), configuring the destination as an attacker-controlled
S3 bucket; when started or scheduled, the new flow exfiltrates SaaS data from the referenced connector
without modifying any existing flow.'