Krypto-Mining in Kubernetes in Audit-Logs erkennen
Krypto-Mining in Kubernetes-Clustern über Audit-Logs erkennen: Miner-Images und -Argumente, unübliche Registries, Persistenz per CronJob und DaemonSet.
TL;DR. Ein Miner im Cluster ist ein Workload, und jeder Workload wird über den API-Server angelegt. Suchen Sie im Audit-Log nach Workload-Schreibzugriffen, deren Image oder Argumente einen Miner (XMRig und Co.) oder einen Mining-Pool (stratum+tcp://, --donate-level) nennen, nach Images aus Registries, die Sie nicht nutzen, und nach der Persistenz drumherum: CronJobs, DaemonSets, oft in kube-system. Finden Sie dann die Identität, die sie angelegt hat: Der Miner ist das Symptom, das gestohlene Zugangsdatum ist der Incident. Ressourcenmissbrauch ist ATT&CK T1496.
Krypto-Mining ist der häufigste Weg, auf dem ein Einbruch in einen Cluster sichtbar wird, weil er Geld und CPU kostet. Er ist auch der am wenigsten interessante Teil der Untersuchung: Wer den Miner deployt hat, hatte genug Zugriff für Schlimmeres und hat es womöglich auch getan.
Ein dokumentiertes Muster
Microsoft hat 2020 eine solche Kampagne beschrieben: Über einen Load Balancer ins Internet exponierte Kubeflow-Dashboards erlaubten es jedem, Container zu deployen, und Angreifer nutzten das, um ein Image mit XMRig auf Dutzenden Clustern laufen zu lassen, die meisten davon auf AKS (Microsoft Security Blog, „Misconfigured Kubeflow workloads are a security risk“). Derselbe Beitrag erklärt, warum solche Cluster attraktiv sind: ML-Nodes sind leistungsstark und haben teils GPUs.
Das Muster ist verbreitet: ein exponiertes Dashboard oder ein überprivilegiertes Token als Einstieg, dann Workloads, die mit den eigenen Zugangsdaten des Clusters angelegt werden. Der Walkthrough des fiktiven Incidents auf dieser Seite folgt derselben Form.
Was das Audit-Log aufzeichnet
Auf Level Request oder RequestResponse für Workload-Schreibzugriffe (Standard auf EKS und GKE, siehe Export-Leitfaden) enthält das requestObject eines Deployments, DaemonSets, Jobs oder CronJobs das Pod-Template: Images, Befehle, Argumente, Ressourcen. Ein fiktiver CronJob:
{
"verb": "create",
"user": { "username": "system:serviceaccount:kube-system:node-health" },
"objectRef": { "resource": "cronjobs", "namespace": "kube-system", "name": "kube-state-sync", "apiGroup": "batch" },
"requestObject": {
"spec": {
"schedule": "*/5 * * * *",
"jobTemplate": { "spec": { "template": { "spec": {
"containers": [{
"name": "sync",
"image": "registry.k8s-mirror.example:5000/xmrig/xmrig:6.21.0",
"args": ["--url=pool.minexmr.example:4444", "--donate-level=0", "--cpu-max-threads-hint=75"],
"resources": { "limits": { "cpu": "2" } }
}]
} } } }
}
}
}
Alles, was Sie brauchen, steckt in einem Event: der Miner, der Pool, ein Name, der ein Kubernetes-Add-on imitiert, ein Namespace, in dem er untergehen soll, eine Registry, die nicht Ihre ist, ein Zeitplan, der die Pods neu anlegt, wenn jemand sie löscht, und ein CPU-Limit, das unter dem Radar bleiben soll.
Erkennungen
| Regel | Schweregrad | Logik |
|---|---|---|
| Crypto-Mining-Image oder -Befehl | Kritisch | Image enthält einen Miner-Marker (xmrig, xmr-stak, minerd, cpuminer, nicehash, kinsing, c3pool, moneroocean…) oder Befehl/Argumente des Containers enthalten stratum+tcp, stratum+ssl, --donate-level, xmrig, minerd; außerdem Falco-Alerts mit passendem Befehl |
| Image aus einer unüblichen Registry | Niedrig | Registry außerhalb einer Liste gängiger öffentlicher und Cloud-Registries (docker.io, registry.k8s.io, gcr.io, ghcr.io, quay.io, mcr.microsoft.com, public.ecr.aws, *.amazonaws.com, *.pkg.dev, *.azurecr.io…) |
| CronJob erstellt | Mittel | Jeder CronJob, den eine Nicht-System-Identität schreibt |
| DaemonSet in kube-system erstellt | Hoch | Siehe privilegierte Pods und Container-Escape |
Die Miner-Regel ist die einzige im Analyzer, die greift, egal wer den Pod angelegt hat, Controller eingeschlossen: Ein von einem CronJob gestarteter Miner erscheint als Pods, die der Job-Controller anlegt, und Sie wollen ihn trotzdem sehen. Gruppiert wird nach Image, ein Finding deckt also alle Replikate ab.
Die Registry-Regel hat bewusst einen niedrigen Schweregrad. Ihre eigene private Registry löst sie aus, bis Sie sie auf die Vertrauensliste setzen; danach fällt alles andere auf.
Mit welchen Ausweichmanövern Sie rechnen sollten
Namensabgleich fängt bequeme Angreifer. Andere:
- benennen Binary und Image um (
alpine,nginx,busyboxmit nachgeladenem Payload); - starten von einem legitimen Image und holen den Miner im Befehl nach (
curl … | sh); - minen über TLS zu einem Proxy auf Port 443 statt zu einem bekannten Pool;
- starten den Miner nach einem Container-Escape auf dem Node, ganz ohne API-Aufruf.
Achten Sie daher auch auf den Kontext, den das Audit-Log aufzeichnet: neue Workloads von Identitäten, die nie deployen, Images aus neuen Registries, command-Felder mit Download-und-Ausführen-Mustern, CronJobs und DaemonSets außerhalb Ihrer Deployment-Pipeline und Workloads, die direkt nach einem neuen cluster-admin-Binding entstehen.
Signale außerhalb des Audit-Logs
Mining ist auf anderen Kanälen laut, und dort fällt es oft zuerst auf:
- CPU- und Node-Metriken: Nodes am Limit, ein Cluster Autoscaler, der ohne geschäftlichen Grund Nodes hinzufügt.
- Ausgehender Traffic: Verbindungen zu Mining-Pools, typischerweise auf Ports wie 3333, 4444 oder 5555, oder zu ungewöhnlichen TLS-Endpunkten.
- Cloud-Abrechnung: ein Sprung bei Compute, manchmal in Regionen, die Sie nicht nutzen.
- Runtime-Erkennung: Falco kann Mining-Verhalten auf Prozess- und Netzwerkebene melden; das Projekt beschreibt den Ansatz in Cryptomining Detection Using Falco. Laden Sie Falco-Alerts als JSON neben den Audit-Logs in den Analyzer, um beides auf einer Timeline zu sehen.
Eine Bereinigung, die wirklich funktioniert
Die Pods zu löschen reicht nicht: CronJob oder DaemonSet legen sie binnen Minuten neu an.
- Sichern: Specs (
kubectl get cronjob,daemonset,deployment -A -o yaml), Audit-Logs und, falls ein Container-Escape möglich ist, Disk-Snapshots der Nodes. - Jede Kopie finden: in allen Namespaces nach Image, Pool-Adresse und Befehl suchen, nicht nur dort, wo Sie fündig wurden.
- Zuerst die Controller entfernen (CronJobs, DaemonSets, Deployments, Jobs), dann verbleibende Pods.
- Den Zugriff entfernen: Binding und Service Account, mit denen sie angelegt wurden, sowie den ursprünglichen Einstiegspunkt.
- Nodes neu aufbauen, wenn der Miner privilegiert oder mit Host-Mounts lief.
- Ausgehenden Traffic zu Mining-Pools blockieren und Egress mit Network Policies beschränken.
- Registries einschränken mit einer Admission-Policy (ValidatingAdmissionPolicy, Kyverno oder Gatekeeper), idealerweise mit Signaturprüfung.
- Abrechnung prüfen und das Cloud-Audit-Log nach allem durchsuchen, was mit denselben Zugangsdaten außerhalb des Clusters angelegt wurde.