Cve 2024 7594 · Research

HashiCorp Vault Flaw (CVE-2024-7594) Allowed Root Host Takeover via Blank SSH Certificates

Threat dossier diagram for CVE-2024-7594 detailing the Vault SSH empty principals vulnerability, CVSS 8.8 score, affected versions, and privilege escalation path to root host access.
AK

Threat intelligence editor · Updated Aug 22, 2026, 5:36 PM EDT

HashiCorp Vault flaw CVE-2024-7594 lets attackers gain root SSH access via blank certificates. Learn root cause analysis, detection scripts, and remediation.

A subtle specification mismatch between enterprise secrets managers and UNIX authentication daemons has left engineering fleets vulnerable to full privilege escalation. Disclosed under CVE-2024-7594 and tracked as HashiCorp security advisory HCSEC-2024-20, the flaw in the HashiCorp Vault SSH secrets engine allows authenticated callers with basic certificate-signing privileges to obtain root shell access on downstream hosts whenever roles omit explicit principal constraints.

The vulnerability affects HashiCorp Vault Community Edition and Enterprise versions 1.7.7 through 1.17.5, legacy enterprise maintenance releases, and the open-source fork OpenBao prior to 2.0.2. Discovered by security researcher Jörn Heissler, the issue carries a National Vulnerability Database rating of CVSS 8.8 (High). While exploitation requires an authenticated token with update permissions on /ssh/sign/:role or /ssh/issue/:role, the operational risk in automated CI/CD pipelines and shared developer bastion environments is substantial.

ParameterTechnical Specification
Vulnerability IDCVE-2024-7594 / HCSEC-2024-20
Vulnerability ClassCWE-732 (Incorrect Permission Assignment for Critical Resource)
CVSS v3.1 Severity8.8 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)
Affected SoftwareVault CE & Enterprise 1.7.71.17.5, 1.16.9, 1.15.14; OpenBao < 2.0.2
Remediated ReleasesVault CE 1.17.6+, Enterprise 1.17.6+, 1.16.10+, 1.15.15+; OpenBao 2.0.2+
PrerequisitesAuthenticated Vault token with write access to an SSH signing role

[[image:poster]]


Architecture of Vault SSH CA and Root Cause of CVE-2024-7594

To avoid managing static SSH keys, organizations use Vault as a short-lived Certificate Authority (CA). Target servers establish trust by setting TrustedUserCAKeys in /etc/ssh/sshd_config to Vault’s public CA key. When a client authenticates using a signed certificate (-cert.pub), the host daemon (sshd) validates the signature, time window, and embedded valid principals.

The flaw stems from an interaction with the OpenSSH specification (PROTOCOL.certkeys). If an OpenSSH certificate contains an empty list of valid principals ([]), OpenSSH treats the certificate as valid for any local operating system account on the target host—including root.

Prior to version 1.17.6, Vault allowed administrators to define SSH roles without setting valid_principals, allowed_users, or default_user. When an authorized caller requested a signature from an unconstrained role without supplying principals in the API payload, Vault generated a cryptographically valid certificate containing an empty principal array. Downstream OpenSSH servers accepted this certificate as a wildcard credential for root interactive logins.

sequenceDiagram
 autonumber
 actor Caller as Authenticated CI Runner / Dev
 participant Vault as Vault SSH Engine (<=1.17.5)
 participant Target as Linux Target (sshd)

 Note over Caller,Vault: Role lacks valid_principals / default_user
 Caller->>Vault: POST /ssh/sign/dev-role {"public_key": "...", "valid_principals": ""}
 Vault-->>Caller: Returns Signed Certificate (Principals: [])
 Note over Caller,Target: OpenSSH treats empty principals as ANY local user
 Caller->>Target: ssh -i id_rsa -o CertificateFile=id_rsa-cert.pub root@host
 Target->>Target: Validates CA signature; detects empty principal array
 Target-->>Caller: Interactive Root Shell Granted

Threat Vectors Across CI/CD and Developer Access

The blast radius of CVE-2024-7594 centers on identity over-privileging and shared CA trust domains:

  • Automated CI/CD Pipeline Hijacking: Build workers (such as GitHub Actions runners or Jenkins agents) often use machine tokens to generate short-lived certificates for low-privilege deploy accounts like app-deployer. If the backing Vault role omits principal constraints, a compromised pipeline step can omit the valid_principals parameter during signing and obtain direct root access on target infrastructure.
  • Privilege Escalation via Shared CAs: Organizations using a single CA root across staging and production allow low-privilege engineers assigned to staging roles to issue wildcard certificates, enabling lateral movement and unauthorized root logins on production servers.
  • Host Defense Deficits: Fleets relying solely on TrustedUserCAKeys without local host-level principal filtering (AuthorizedPrincipalsFile) lack any defensive barrier against empty-principal certificates.

Automated Policy Enforcement with OPA and Sentinel

HashiCorp patched the issue by adding the allow_empty_principals configuration option to SSH roles, setting it to false by default. Under patched versions, Vault rejects signing requests if the resulting certificate would contain zero principals.

Platform teams can enforce this restriction in Terraform configurations using Open Policy Agent (OPA) Rego policies:

package terraform.vault.ssh_security

default allow = false

# Deny unconstrained or explicitly permissive SSH roles
deny[msg] {
 resource := input.resource_changes[_]
 resource.type == "vault_ssh_secret_backend_role"

 # Block explicit empty principal allowance
 resource.change.after.allow_empty_principals == true
 msg := sprintf("CRITICAL: Vault role '%v' sets allow_empty_principals=true", [resource.address])
}

deny[msg] {
 resource := input.resource_changes[_]
 resource.type == "vault_ssh_secret_backend_role"
 object.get(resource.change.after, "key_type", "ca") != "host"

 # Ensure at least one constraint is populated
 object.get(resource.change.after, "allowed_users", "") == ""
 object.get(resource.change.after, "valid_principals", "") == ""
 object.get(resource.change.after, "default_user", "") == ""
 object.get(resource.change.after, "allowed_users_template", false) == false

 msg := sprintf("HIGH: Vault role '%v' missing required principal constraints (CVE-2024-7594)", [resource.address])
}

allow { count(deny) == 0 }

For teams enforcing API governance directly at the Vault boundary, this HashiCorp Sentinel Endpoint Governing Policy (EGP) blocks insecure role definitions:

import "strings"
import "types"

is_ssh_role_write = rule {
 request.operation in ["create", "update"] and
 strings.has_prefix(request.path, "ssh/roles/")
}

main = rule when is_ssh_role_write {
 request.data.allow_empty_principals is not true and
 ((types.type_of(request.data.allowed_users) is "string" and length(request.data.allowed_users) > 0) or
 (types.type_of(request.data.valid_principals) is "string" and length(request.data.valid_principals) > 0) or
 (types.type_of(request.data.default_user) is "string" and length(request.data.default_user) > 0) or
 (request.data.allowed_users_template is true))
}

Live Cluster Auditing and Historical Log Telemetry

Security teams can audit running clusters and detect past exploitation attempts using CLI discovery scripts and log analysis.

Cluster Audit Script

This Bash script iterates through all SSH mounts and flags roles susceptible to empty-principal generation:

#!/usr/bin/env bash
set -euo pipefail

echo "[*] Scanning Vault for Unconstrained SSH Roles (CVE-2024-7594)..."
for MOUNT in $(vault secrets list -format=json | jq -r 'to_entries[] | select(.value.type == "ssh") | .key'); do
 for ROLE in $(vault list -format=json "${MOUNT}roles" 2>/dev/null | jq -r '.[]' || true); do
 CFG=$(vault read -format=json "${MOUNT}roles/${ROLE}")
 USERS=$(echo "$CFG" | jq -r '.data.allowed_users // ""')
 PRINCIPALS=$(echo "$CFG" | jq -r '.data.valid_principals // ""')
 DEF_U=$(echo "$CFG" | jq -r '.data.default_user // ""')
 ALLOW_EMPTY=$(echo "$CFG" | jq -r '.data.allow_empty_principals // false')
 TPL=$(echo "$CFG" | jq -r '.data.allowed_users_template // false')

 if [ "$(echo "$CFG" | jq -r '.data.key_type // "ca"')" != "host" ]; then
 if [ "$ALLOW_EMPTY" = "true" ] || ([ -z "$USERS" ] && [ -z "$PRINCIPALS" ] && [ -z "$DEF_U" ] && [ "$TPL" != "true" ]); then
 echo " [!] VULNERABLE ROLE: ${MOUNT}roles/${ROLE}"
 fi
 fi
 done
done

Audit Log Telemetry and Host Correlation

To check for historical exploitation, parse Vault audit logs for signing requests that lacked valid principals:

cat /var/log/vault/audit.log | jq -r '
 select(.type == "request" and (.request.path | test("ssh/(sign|issue)/"))) |
 select(.request.data.valid_principals == null or .request.data.valid_principals == "" or .request.data.valid_principals == []) |
 { timestamp: .time, identity: .auth.display_name, entity: .auth.entity_id, path: .request.path, ip: .request.remote_address }
'

Correlate matching log timestamps with host authentication logs (/var/log/secure or /var/log/auth.log). An unauthorized entity whose token generated a blank-principal certificate and logged in under Accepted publickey for root confirms a privilege escalation incident.


Safe Migration and Host-Level Defense-in-Depth

Remediating production clusters without breaking active developer access or automated pipelines requires a structured, phased rollout:

  1. Role Refactoring with Identity Templates: Dynamically bind allowed principals to verified user metadata instead of relying on client-supplied values:
vault write ssh-client-signer/roles/dev-access \
 key_type="ca" \
 algorithm_signer="rsa-sha2-512" \
 allow_user_certificates=true \
 allowed_users_template=true \
 allowed_users="{{identity.entity.name}},{{identity.entity.aliases.auth_oidc_accessor.metadata.username}}" \
 default_user="{{identity.entity.name}}" \
 ttl="4h" \
 allow_empty_principals=false
  1. Dual-Role Migration: Create strict canary roles alongside legacy endpoints. Update CI/CD pipelines to explicitly declare target accounts before removing legacy roles.
  2. Upgrade Engine: Upgrade Vault binaries to 1.17.6+ (or maintenance releases 1.16.10+, 1.15.15+).
  3. Host-Level Defense-in-Depth: Configure /etc/ssh/sshd_config to enforce principal validation locally:
TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem
AuthorizedPrincipalsFile /etc/ssh/authorized_principals/%u

Placing explicit permitted identifiers (such as admin-breakglass) in /etc/ssh/authorized_principals/root ensures target daemons reject certificates with missing or invalid principals, eliminating wildcard escalation even if upstream CA misconfigurations occur.