Back to Intelligence

CVE-2026-12564: Critical AWX/AAP HashiCorp Vault Plugin Flaw Leaks Kubernetes Service Account Tokens — Detection and Remediation Guide

SA
Security Arsenal Team
August 18, 2026
9 min read

NVD has published CVE-2026-12564, a CVSS 9.6 (Critical), network-exploitable vulnerability in the HashiCorp Vault credential plugin shipped with the AWX / Ansible Automation Platform (AAP) Controller. The flaw lives in the kubernetes_auth() function inside awx_plugins/credentials/hashivault.py. When a user creates a HashiCorp Vault Secret Lookup credential configured with kubernetes_role authentication and clicks Test, the function reads the controller pod's own Kubernetes service account token from disk and transmits it to whatever Vault URL the credential is configured to reach — including an attacker-controlled endpoint.

The result is not a marginal information disclosure. The controller pod's service account in typical AAP-on-OpenShift/Kubernetes deployments carries permissions to the control-plane namespaces: full pod CRUD and secret read. That means database credentials, automation secrets, and effectively the keys to the entire automation estate. The only prerequisite for the attacker is an authenticated account with credential-creation privileges in AWX/AAP — a permission set routinely granted to automation engineers, team leads, and any integrated CI/CD service accounts.

If you run AWX upstream, or Red Hat Ansible Automation Platform deployed on OpenShift or Kubernetes with the HashiCorp Vault credential type in use, treat this as an urgent remediation item. Every 'Test' click against a malicious Vault URL is a silent token hand-off.

Technical Analysis

Affected Component

  • Product: AWX / Red Hat Ansible Automation Platform (AAP) Controller — HashiCorp Vault credential plugin
  • Affected file: awx_plugins/credentials/hashivault.py
  • Affected function: kubernetes_auth()
  • Deployment context: Controller running as a pod on Kubernetes / OpenShift, where a service account token is mounted at the standard projected token path (/var/run/secrets/kubernetes.io/serviceaccount/token)
  • Reference: NVD — CVE-2026-12564

Attack Chain

  1. Prerequisite access: The attacker holds (or compromises) any AWX/AAP account with permission to create or modify Credentials. In many organizations this is a broad group — anyone building job templates against external secret stores.
  2. Malicious credential configuration: The attacker creates a HashiCorp Vault Secret Lookup credential, selects Kubernetes authentication (kubernetes_role), and points the Vault server URL at infrastructure they control.
  3. Trigger: The attacker clicks Test on the credential (or triggers it via API). This causes the controller to invoke kubernetes_auth().
  4. Token read and exfiltration: kubernetes_auth() reads the controller pod's own mounted service account JWT and sends it to the attacker-specified URL as part of the Vault Kubernetes auth login flow. There is no validation that the destination is a legitimate, organization-approved Vault cluster.
  5. Control-plane compromise: The attacker replays the captured JWT against the Kubernetes API server. With the controller service account's typical RBAC posture, this yields pod create/read/update/delete and secret read in the AAP namespace — exposing database credentials, automation execution secrets, and enabling lateral movement into adjacent workloads.

Why the CVSS 9.6 Is Justified

  • Network vector, low complexity: exploitation is an authenticated API/UI action — no race conditions, no special conditions.
  • Scope change: the impact escapes AWX entirely and lands in the Kubernetes control plane.
  • Confidentiality/Integrity/Availability impact: secret read plus pod CRUD means full compromise of the automation platform's data and execution environment.
  • The mitigating factor — required credential-creation privileges — is why this scores 9.6 rather than 10.0. Do not let that lull you: privilege-creation rights are commonly delegated, and this is a textbook privilege escalation via confused deputy pattern.

Exploitation Status

As of publication, the CVE has been published by NVD with the technical mechanism fully documented above. No CISA KEV listing has been confirmed at the time of writing, but the exploitation path requires zero exploit code — it is a configuration abuse against a documented code path. Assume motivated adversaries with valid AWX access (insider, phished credential, or compromised CI account) can execute this today. Any environment where AWX/AAP credential management is exposed to a broad user base should treat this as exploitable in practice, not theoretical.

Detection & Response

The defensive signal here is strong and narrow. You are hunting two observable behaviors: (1) outbound connections from the AWX/AAP controller pod to unexpected Vault endpoints, and (2) AWX API/audit events showing creation or testing of HashiCorp Vault credentials with Kubernetes auth. Both are low-noise in a well-inventoried environment.

YAML
---
title: AWX/AAP Controller Pod Outbound Connection to Non-Standard Vault Endpoint
id: 3f8c2a14-7b61-4e59-a3d2-9c1e5f7a2b48
status: experimental
description: Detects the AWX/AAP controller process initiating outbound connections to endpoints that are not the organization's approved HashiCorp Vault address, consistent with CVE-2026-12564 service account token exfiltration via a malicious Vault Secret Lookup credential.
references:
  - https://nvd.nist.gov/vuln/detail/CVE-2026-12564
  - https://attack.mitre.org/techniques/T1552/
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.credential_access
  - attack.t1552
  - attack.exfiltration
logsource:
  category: network_connection
  product: linux
detection:
  selection_process:
    Image|contains:
      - 'awx'
      - 'python'
    Initiated: 'true'
  selection_vault_port:
    DestinationPort:
      - 8200
      - 8201
  filter_approved_vault:
    DestinationIp|contains:
      - '10.0.0.0/8'  # TODO: replace with approved Vault cluster CIDRs
  condition: selection_process and selection_vault_port and not filter_approved_vault
falsepositives:
  - Legitimate Vault clusters not yet added to the approved destination filter
  - Vault disaster-recovery or test instances under administrative control
level: high
---
title: Suspicious Vault Kubernetes Auth Login from Unexpected Source
id: 8d4e6b72-1a93-4c28-b5e7-6f2a9d3c5e01
status: experimental
description: Detects HashiCorp Vault kubernetes auth login requests originating outside the expected controller pod identity, indicating replay of an exfiltrated service account token per CVE-2026-12564.
references:
  - https://nvd.nist.gov/vuln/detail/CVE-2026-12564
  - https://attack.mitre.org/techniques/T1078/
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.initial_access
  - attack.t1078.004
logsource:
  category: webserver
  product: linux
detection:
  selection:
    cs-uri-stem|contains:
      - '/v1/auth/kubernetes/login'
    cs-method: 'POST'
falsepositives:
  - Legitimate Vault Kubernetes auth logins from enrolled workloads — tune by source address allowlist
level: medium

Tuning note: the first rule only earns its keep if you replace the placeholder CIDR with your actual approved Vault cluster addresses. An untuned version will fire on every legitimate Vault test and get disabled within a week. Do the inventory work first.

KQL — Microsoft Sentinel / Defender
// Hunt: Outbound connections from AWX/AAP controller pods to Vault ports
// destined for addresses outside the approved Vault inventory.
// Requires Syslog/CEF ingestion of pod or node-level network telemetry,
// or Defender for Endpoint on the node.
let ApprovedVaultIPs = dynamic(["10.20.5.10", "10.20.5.11"]); // TODO: approved Vault cluster IPs
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where RemotePort in (8200, 8201)
| where InitiatingProcessFileName has_any ("python", "awx")
   or InitiatingProcessCommandLine has_any ("awx", "hashivault", "credential")
| where not (RemoteIP has_any (ApprovedVaultIPs))
| project TimeGenerated, DeviceName, InitiatingProcessFileName,
          InitiatingProcessCommandLine, RemoteIP, RemotePort, RemoteUrl
| order by TimeGenerated desc;

// Companion hunt: Syslog/CEF for Vault kubernetes auth login attempts
// replaying an exfiltrated SA token from unexpected source IPs.
CommonSecurityLog
| where TimeGenerated > ago(7d)
| where RequestURL has "/v1/auth/kubernetes/login"
| where RequestMethod =~ "POST"
| extend SourceIP = coalesce(SourceIP, DeviceAddress)
| summarize LoginAttempts = count(), DistinctSources = dcount(SourceIP)
    by SourceIP, bin(TimeGenerated, 1h)
| order by LoginAttempts desc;
VQL — Velociraptor
-- Hunt: Identify reads of the Kubernetes service account token by
-- non-standard processes on AWX/AAP controller nodes/pods.
-- Deploy against controller pod hosts; correlate with AWX credential-test audit events.
SELECT Pid, Name, CommandLine, Exe, Username, CreateTime
FROM pslist()
WHERE CommandLine =~ 'hashivault|kubernetes_auth|credential'
   OR Exe =~ 'awx'

-- Also audit which processes hold the SA token path open or reference it:
SELECT Pid, Name, CommandLine
FROM pslist()
WHERE CommandLine =~ '/var/run/secrets/kubernetes.io/serviceaccount/token'

Response actions on a confirmed hit:

  1. Immediately rotate the controller pod's service account token — delete the pod (Kubernetes issues a fresh projected token on recreation) and invalidate any bound tokens: kubectl delete pod <awx-controller-pod> -n <namespace>.
  2. Audit AWX credentials: enumerate all HashiCorp Vault Secret Lookup credentials and review their configured Vault URLs against your approved inventory. AWX logs credential create/update/test events in its activity stream — review via the API at /api/v2/activity_stream/.
  3. Review Kubernetes audit logs for API calls authenticated with the controller service account identity from outside the cluster's expected pod IPs.
  4. If replay is confirmed, assume namespace secret compromise: rotate database credentials and any automation secrets stored in the AAP namespace.

Remediation

  1. Patch first. Apply the vendor fix as soon as Red Hat / AWX publish patched builds for CVE-2026-12564. Track the NVD entry (https://nvd.nist.gov/vuln/detail/CVE-2026-12564) and the Red Hat Security Data API / RHSA feed for your AAP channel. Verify the patched version in your environment with the script below — do not assume an operator upgrade covered the plugin code.
  2. Least privilege on credential management. Immediately audit which AWX/AAP users and service accounts hold credential-create/modify rights. Restrict to a named, minimal admin group. This is the single highest-impact compensating control: it collapses the attacker prerequisite.
  3. Egress control on the controller pod. Enforce a Kubernetes NetworkPolicy (or Cilium/Calico equivalent) restricting the AAP controller namespace egress on TCP 8200/8201 to your approved Vault cluster IPs only. This breaks the exfiltration path even before patching.
  4. Rotate the service account token as a precaution in any environment where untrusted users had credential-creation rights, and review AWX activity stream history for suspicious Vault credential tests.
  5. Reduce the blast radius of the controller SA. Re-scope the controller service account's RBAC: it should not hold blanket secret-read across the namespace unless your deployment strictly requires it. Apply least-privilege ClusterRole bindings.

The following Bash script verifies the vulnerable code path is present and validates that egress controls are in place:

Bash / Shell
#!/bin/bash
# CVE-2026-12564 verification and hardening check for AWX/AAP controller pods
# Run from a host with cluster-admin kubectl access.

NS="ansible-automation-platform"   # adjust to your AAP namespace

echo "=== [1] Locate controller pods ==="
kubectl get pods -n "$NS" -l app.kubernetes.io/component=automationcontroller -o wide

echo "=== [2] Check hashivault.py for the vulnerable kubernetes_auth pattern ==="
POD=$(kubectl get pods -n "$NS" -l app.kubernetes.io/component=automationcontroller -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n "$NS" "$POD" -- \
  grep -n "def kubernetes_auth" /usr/lib/python3*/site-packages/awx_plugins/credentials/hashivault.py 2>/dev/null \
  || echo "hashivault.py not found at default path - locate manually with: find / -name hashivault.py"

echo "=== [3] Confirm service account token mount exists ==="
kubectl exec -n "$NS" "$POD" -- ls -l /var/run/secrets/kubernetes.io/serviceaccount/token

echo "=== [4] Review NetworkPolicies applied to the namespace ==="
kubectl get networkpolicies -n "$NS"
echo "If no egress policy restricts 8200/8201 to approved Vault IPs, apply one NOW."

echo "=== [5] Enumerate Vault-type credentials (requires AWX admin token) ==="
echo "curl -sk -H \"Authorization: Bearer \$AWX_TOKEN\" https://<awx-host>/api/v2/credentials/?credential_type__namespace=hashivault | jq '.results[] | {id, name, modified}'"

echo "=== [6] Force SA token rotation by restarting the controller pod ==="
echo "kubectl delete pod $POD -n $NS   # execute only after confirming exposure"

Post-patch validation: after upgrading, re-run step 2 and confirm the kubernetes_auth() implementation now validates the Vault destination against a configured/expected address, or no longer transmits the pod token on credential test. Add a regression check to your upgrade runbook.

Related Resources

Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.