aws-msk-connect-can-modify-code

kafkaconnect:UpdateConnector with a new custom plugin revision replaces the connector's executable code, enabling arbitrary code execution as its service execution role.

derived aws emits CanModifyCode

match (effective permission)

{ "action": "kafkaconnect:UpdateConnector", "resource_type": "AWS::MSKConnect::Connector" }

where

connector uses a CUSTOM plugin (customPlugin config present, not a built-in Confluent/community connector)

emit

source typeIdentity
target typeGenericCompute
source<principal>
target<MSK Connect Connector (GenericCompute)>
permissionskafkaconnect:UpdateConnector
conditionsiam_permission service_state
state logicACTIVE when the principal has effective kafkaconnect:UpdateConnector on the connector scope AND the connector uses a custom plugin (the UpdateConnector API accepts customPlugin.revision to switch to a new plugin version). When the connector uses only built-in connectors (no custom plugin), UpdateConnector only changes configuration parameters (task count, settings) — emit CanModifyConfiguration (B3) instead, not CanModifyCode. BLOCKED if SCP or permission boundary denies kafkaconnect:UpdateConnector. Note that deploying a new custom plugin version also requires kafkaconnect:CreateCustomPlugin to upload the JAR/ZIP; the combined capability (CreateCustomPlugin + UpdateConnector) is the full code-replacement path.

Narrative

{principal.name} can call kafkaconnect:UpdateConnector on {connector.name} with a new custom plugin revision, replacing the connector's executable code (JAR/ZIP); the running connector tasks are restarted with the attacker-supplied code, which executes as {role.name} (the connector's service execution role).

Raw rule rules/derived/aws/msk.yaml

id: aws-msk-connect-can-modify-code
emits: CanModifyCode
description: kafkaconnect:UpdateConnector with a new custom plugin revision replaces the connector's executable
  code, enabling arbitrary code execution as its service execution role.
match_effective_permission:
  action: kafkaconnect:UpdateConnector
  resource_type: AWS::MSKConnect::Connector
where:
- connector uses a CUSTOM plugin (customPlugin config present, not a built-in Confluent/community connector)
emit:
  source_type: Identity
  target_type: GenericCompute
  source: <principal>
  target: <MSK Connect Connector (GenericCompute)>
  permissions:
  - kafkaconnect:UpdateConnector
  conditions:
  - iam_permission
  - service_state
  state_logic: "ACTIVE when the principal has effective kafkaconnect:UpdateConnector on the connector\
    \ scope AND the connector uses a custom plugin (the UpdateConnector API accepts customPlugin.revision\
    \ to switch to a new plugin version). When the connector uses only built-in connectors (no custom\
    \ plugin), UpdateConnector only changes configuration parameters (task count, settings) \u2014 emit\
    \ CanModifyConfiguration (B3) instead, not CanModifyCode. BLOCKED if SCP or permission boundary denies\
    \ kafkaconnect:UpdateConnector. Note that deploying a new custom plugin version also requires kafkaconnect:CreateCustomPlugin\
    \ to upload the JAR/ZIP; the combined capability (CreateCustomPlugin + UpdateConnector) is the full\
    \ code-replacement path."
  derived_from:
  - effective kafkaconnect:UpdateConnector on connector scope
  - connector.customPlugin config present
  false_positive_note: "Only emit CanModifyCode (vs. CanModifyConfiguration) when the connector is confirmed\
    \ to use a custom plugin ARN. Built-in connectors (Debezium, Confluent S3 sink, etc. deployed without\
    \ a custom plugin ARN in the kafkaconnect API response) are not code-modifiable via UpdateConnector\
    \ alone \u2014 the plugin binary is managed by AWS. When the custom plugin status is UNKNOWN, emit\
    \ CanModifyConfiguration conservatively (B3). The can-execute-as linchpin execute-as-via-code-modify\
    \ consumes this CanModifyCode edge plus the ExecutesAs(connector \u2192 role) fact (B1) to derive\
    \ CanExecuteAs \u2014 cite, do not re-derive."
  narrative: '{principal.name} can call kafkaconnect:UpdateConnector on {connector.name} with a new custom
    plugin revision, replacing the connector''s executable code (JAR/ZIP); the running connector tasks
    are restarted with the attacker-supplied code, which executes as {role.name} (the connector''s service
    execution role).'
move · open · esc close