Files
hacktricks-cloud/src/pentesting-ci-cd/argocd-security.md
T

12 KiB

Argo CD Security

{{#include ../banners/hacktricks-training.md}}

Basic Information

Argo CD is a GitOps continuous delivery platform for Kubernetes. It watches Git repositories, renders Kubernetes manifests with tools such as Helm, Kustomize, Jsonnet or config management plugins, and reconciles the live cluster state with the desired state stored in Git.

From an attacker perspective, treat Argo CD as a deployment engine with Kubernetes credentials. A useful Argo CD compromise can lead to:

  • Access to private Git repositories and repository credentials.
  • Access to Kubernetes cluster secrets used by Argo CD.
  • Manifest generation code execution in argocd-repo-server.
  • Unauthorized Kubernetes object deployment through trusted Git repositories, Argo CD applications, or cache manipulation.

Architecture & Interesting Components

Common Kubernetes objects and services:

kubectl get pods,svc,endpoints,ingress -A | grep -iE 'argocd|argo-cd'
kubectl get applications,appprojects,applicationsets -A 2>/dev/null
kubectl get secrets,configmaps -n argocd 2>/dev/null
kubectl get networkpolicy -n argocd 2>/dev/null

Interesting services:

  • argocd-server: public API, web UI, CLI API, authentication and authorization.
  • argocd-application-controller: compares desired and live state, then applies resources to Kubernetes.
  • argocd-repo-server: clones repositories, caches Git data, and runs Helm/Kustomize/Jsonnet/plugins to generate manifests. The default gRPC port is 8081.
  • argocd-redis: cache for application, manifest and Git reference data. The default Redis port is 6379.
  • argocd-applicationset-controller: generates Argo CD Application objects from generators such as Git, SCM, clusters and pull requests.

From a compromised pod or internal network segment, check internal reachability:

nc -vz <argocd-server> 443
nc -vz <argocd-repo-server> 8081
nc -vz <argocd-redis> 6379

Public API / UI Attacks

If you have Argo CD credentials or an exposed instance, start with the normal API surface:

argocd login <argocd-server>
argocd account get-user-info
argocd account list
argocd proj list
argocd app list
argocd repo list
argocd cluster list
argocd admin settings rbac can <subject> <action> <resource> <object>

Useful attack paths:

  • Application write access: modify source.repoURL, source.path, Helm values, Kustomize options, plugin settings or sync options so Argo CD deploys attacker-controlled manifests.
  • Project misconfiguration: AppProject objects can allow broad sourceRepos, broad destinations, unsafe clusterResourceWhitelist, or weak namespace restrictions.
  • Repository credential abuse: repository secrets, GitHub App credentials, SSH keys and tokens can allow pushing to trusted repos or adding malicious dependencies.
  • Cluster credential abuse: cluster secrets may contain bearer tokens or exec-provider configuration used by Argo CD to deploy into target clusters.
  • Local admin / project tokens: long-lived Argo CD tokens can be reused through the API unless revoked or expired.

Enumerate configuration from Kubernetes when you have cluster read access:

kubectl get applications.argoproj.io -A -o yaml
kubectl get appprojects.argoproj.io -A -o yaml
kubectl get applicationsets.argoproj.io -A -o yaml
kubectl get secrets -n argocd -o yaml | grep -nE 'repoURL|sshPrivateKey|password|bearerToken|githubApp|tlsClientCertData|tlsClientCertKey'
kubectl get cm -n argocd argocd-cm argocd-rbac-cm argocd-cmd-params-cm -o yaml

Trusted Git Repository Abuse

If you can push to a repository trusted by Argo CD, you can usually influence what is deployed. Impact depends on the AppProject boundaries and the service account permissions used by the application controller.

Common payload locations:

  • Raw Kubernetes YAML under an application path.
  • Helm chart templates and values.yaml.
  • Kustomize overlays, remote bases and generators.
  • Jsonnet or config management plugin input.
  • ApplicationSet generator files that create or update Application objects.

Check whether the app uses automated sync, pruning, self-heal, sync windows or manual approvals:

kubectl get applications.argoproj.io -A \
  -o custom-columns='NS:.metadata.namespace,APP:.metadata.name,PROJECT:.spec.project,AUTOSYNC:.spec.syncPolicy.automated,REPO:.spec.source.repoURL,PATH:.spec.source.path,DEST:.spec.destination.server'

Direct argocd-repo-server Abuse

Do not assume the public Argo CD API is the only attack surface. Internal Argo CD components communicate with argocd-repo-server over gRPC. If arbitrary pods can reach repo-server, attacker-controlled internal requests may bypass checks normally enforced by argocd-server.

Practical checks:

kubectl get svc -n argocd argocd-repo-server -o yaml
kubectl get endpoints -n argocd argocd-repo-server -o wide
nc -vz <argocd-repo-server> 8081

Interesting signs:

  • The repo-server gRPC endpoint is reachable from non-Argo CD pods.
  • NetworkPolicies are missing or only allow-list egress without denying ingress.
  • The repo-server has access to custom config management plugins, decryption tools, or repository content from multiple tenants.
  • Redis is reachable from non-Argo CD pods, allowing cache inspection or tampering if credentials are available or not required.

Unauthenticated Repo-Server RCE via Kustomize Options

In July 2026, Synacktiv disclosed an unauthenticated code execution chain in Argo CD's repo-server when an attacker can reach the internal gRPC service. The attack abuses direct access to /repository.RepoServerService/GenerateManifest and attacker-controlled KustomizeOptions.

The dangerous primitive is forcing repo-server to clone attacker-controlled repository content and run Kustomize with Helm support:

kustomize build <attacker_repo_path> --enable-helm --helm-command ./payload.sh

Minimal malicious Kustomize input needs to trigger Helm processing:

helmCharts:
- name: pwn
  version: 0.0.1

Why this works:

  • argocd-repo-server clones the repository before rendering.
  • --helm-command ./payload.sh resolves relative to the cloned repository.
  • Code execution does not require shell metacharacter injection if the attacker can control the rendered repository and Kustomize build options.

At the time of Synacktiv's July 1, 2026 disclosure, they reported that the issue had no official fix or CVE. Treat this as a network-exposure issue first: exploitation requires reachability to the internal repo-server gRPC port.

Redis Cache Poisoning to Deploy Manifests

After code execution in argocd-repo-server, or after direct access to Redis with valid credentials, inspect Redis-backed cache entries. Argo CD commonly stores gzip-compressed JSON values.

Interesting key prefixes:

mfst|...       # cached rendered manifests
git-refs|...   # Git branch/ref to commit mappings
app|...        # application resource/cache data
cluster|...    # cluster cache information

The cache poisoning attack described by Synacktiv abuses two pieces of state:

  1. Modify the relevant mfst|... manifest cache entry to include an attacker-controlled Kubernetes manifest.
  2. Modify the related git-refs|... mapping so Argo CD believes the branch moved and then reconciles back to the cached revision.

Impact:

  • With Auto Sync enabled, Argo CD may apply the poisoned cached manifest automatically.
  • Without Auto Sync, the payload may still apply when a user manually syncs the application.
  • The final impact is bounded by the target application's destination and the Kubernetes permissions available to Argo CD.

ApplicationSet Attacks

ApplicationSet is especially sensitive because it creates or updates Application objects from generator output.

Review:

kubectl get applicationsets.argoproj.io -A -o yaml
kubectl get appprojects.argoproj.io -A -o yaml

Interesting patterns:

  • Git generators reading attacker-writable files that control app names, paths, projects or destinations.
  • Pull request generators for public repositories where untrusted contributors can influence generated applications.
  • Template fields that allow broad destination clusters/namespaces.
  • AppProjects that permit sourceRepos: ["*"] or broad destinations.
  • Generated applications that inherit automated sync and pruning.

Post-Exploitation

From an Argo CD pod shell, prioritize:

env
cat /proc/1/environ 2>/dev/null | tr '\0' '\n'
find /var/run/secrets /app/config -type f -maxdepth 4 2>/dev/null
mount | grep -E 'secret|token|config'

Useful objectives:

  • Steal REDIS_PASSWORD or Redis TLS/client material.
  • Extract repository credentials from mounted secrets or Argo CD Kubernetes secrets.
  • Identify cluster credentials used by Argo CD.
  • Read generated manifests and plugin output that may include injected secrets.
  • Check whether custom plugins, SOPS, Helm secrets, Vault plugins or cloud CLIs expose decryption keys and cloud credentials.

Detection & Hardening

Important checks:

  • Restrict argocd-repo-server port 8081 and Redis port 6379 with NetworkPolicies so only expected Argo CD components can reach them.
  • In Helm deployments, verify network policies are actually created. Argo CD Helm chart values have historically defaulted component network policy creation to disabled.
  • Keep argocd-server as the authenticated entry point. Internal services should not be reachable from arbitrary workloads.
  • Disable unused config management tools and plugins.
  • Restrict AppProject sourceRepos, destinations, namespace permissions and cluster-scoped resources.
  • Avoid storing broad repository credentials where a low-privileged Argo CD user can cause their reuse.
  • Monitor repo-server requests, Kustomize build options, plugin executions, Redis writes and unexpected access to mfst| / git-refs| keys.
  • Rotate Argo CD local users, project tokens, repository credentials and cluster credentials after compromise.

Useful commands:

kubectl get networkpolicy -n argocd
kubectl get networkpolicy -A | grep -i argocd
kubectl describe networkpolicy -n argocd argocd-repo-server-network-policy 2>/dev/null
kubectl describe networkpolicy -n argocd argocd-redis-network-policy 2>/dev/null

Static Analysis Note: Typed API Requests in CodeQL

For Go services using gRPC/REST handlers, default CodeQL remote sources may miss flows once raw input has been unmarshaled into typed request objects. A useful model for Argo CD-style services is:

  • Receiver type such as Server or Service.
  • First parameter is context.Context.
  • Second parameter is a typed request object.

Model that second parameter as a remote source and add custom sinks for exec.Command / exec.CommandContext arguments. This helps find flows from internal API request fields into command execution helpers.

References