# Argo CD Security {{#include ../banners/hacktricks-training.md}} ## Basic Information [Argo CD](https://argo-cd.readthedocs.io/) 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: ```bash 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: ```bash nc -vz 443 nc -vz 8081 nc -vz 6379 ``` ## Public API / UI Attacks If you have Argo CD credentials or an exposed instance, start with the normal API surface: ```bash argocd login 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 ``` 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: ```bash 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: ```bash 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: ```bash kubectl get svc -n argocd argocd-repo-server -o yaml kubectl get endpoints -n argocd argocd-repo-server -o wide nc -vz 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: ```bash kustomize build --enable-helm --helm-command ./payload.sh ``` Minimal malicious Kustomize input needs to trigger Helm processing: ```yaml 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: ```text 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: ```bash 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: ```bash 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: ```bash 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 - [Synacktiv - Caught in the Octopus Trap: Unauthenticated RCE in Argo CD with CodeQL](https://www.synacktiv.com/en/publications/caught-in-the-octopus-trap-unauthenticated-rce-in-argo-cd-with-codeql) - [Argo CD docs - Security considerations](https://argo-cd.readthedocs.io/en/stable/operator-manual/security/) - [Argo CD docs - High Availability](https://argo-cd.readthedocs.io/en/stable/operator-manual/high_availability/) - [Argo CD docs - repo-server command reference](https://argo-cd.readthedocs.io/en/stable/operator-manual/server-commands/argocd-repo-server/) - [Argo CD - repo-server NetworkPolicy manifest](https://github.com/argoproj/argo-cd/blob/master/manifests/base/repo-server/argocd-repo-server-network-policy.yaml) - [Argo CD docs - metrics](https://argo-cd.readthedocs.io/en/latest/operator-manual/metrics/) - [Argo Helm - chart values reference](https://github.com/argoproj/argo-helm/blob/main/charts/argo-cd/README.md) - [Kustomize - Helm chart generator example](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/chart.md) {{#include ../banners/hacktricks-training.md}}