Was Cloudflares Container-Sicherheitslücke uns über die Sicherheit von Multi-Tenant-Game-Backends lehrte
Kurz und knapp
Auditieren Sie Ihre dm-thin-Pools und erkennen Sie I/O-Anomalien, um Multi-Tenant-Game-Backends vor Cross-Tenant-Datenlecks zu schützen.
Ihr containerisiertes Game-Backend startet für jedes Match, jede Lobby und jede Analyse-Batch eine frische VM. Wenn der Container heruntergefahren wird, sind die Daten weg — oder?
Nicht unbedingt. Im September 2026 entdeckte der Sicherheitsforscher Oren Yomtov von Accomplish, dass Cloudflare Containers eine Cross-Tenant-Datenoffenlegungsschwachstelle aufwiesen. Restliche Disk-Blöcke zerstörter Container waren für unabhängige Workloads auf demselben Host lesbar. Die wiederhergestellten Daten umfassten Verzeichnisstrukturen, Datenbank-Seiten und strukturell vollständige SQLite-Datenbanken.
Wenn Sie Game-Backends auf einer Multi-Tenant-Container-Plattform betreiben, müssen Sie die Grundursache dieser Schwachstelle verstehen — eine Linux-Speicherkonfiguration namens skip_block_zeroing im dm-thin-Subsystem. Dieser Beitrag erläutert, was schiefgelaufen ist, liefert ein Detektions- und Remediation-Runbook, das Sie sofort anwenden können, und behandelt die architektonischen Prinzipien, die verhindern, dass diese Klasse von Schwachstellen die Daten Ihrer Spieler erreicht.
Wie Thin Provisioning Cross-Tenant-Datenoffenlegung erzeugt
Cloudflare Containers führen Workloads in Firecracker-Micro-VMs auf Multi-Tenant-Hardware aus. Jeder Container erhält eine beschreibbare Root-Disk, die auf Linux-Device-Mapper-Thin-Provisioning (dm-thin) basiert. Firecracker stellt diese Disk der Gast-VM als /dev/vdc bereit.
Thin Provisioning funktioniert, indem die physische Speicherzuweisung aufgeschoben wird. Wenn Sie eine virtuelle 10-GiB-Disk erstellen, verbraucht sie fast keinen physischen Speicherplatz. Blöcke werden bei Bedarf aus einem gemeinsamen Pool zugewiesen, wenn der Gast in eine zuvor nicht zugeordnete Region schreibt. Die betroffenen Pools von Cloudflare verwendeten eine Thin-Block-Größe von 64 KiB.
Hier liegt die Schwachstelle. Wenn ein Container zerstört wird, werden seine Thin-Volume-Zuordnungen gelöscht, und die physischen Blöcke kehren zur Wiederverwendung in den gemeinsamen Pool zurück. Der betroffene Pool war konfiguriert mit:
skip_block_zeroing
Mit diesem Flag überspringt dm-thin das Nullsetzen neu zugewiesener Blöcke, bevor sie für den nächsten Tenant zugänglich gemacht werden. Ein vollständiger 64-KiB-Schreibvorgang überschreibt den gesamten recycelten Block. Aber ein partieller Schreibvorgang — sagen wir 4 KiB — ersetzt nur diese Region. Die verbleibenden 60 KiB können Daten des vorherigen Besitzers des Blocks enthalten.
Das ist kein theoretischer Randfall. Der Forscher stellte Verzeichnisstrukturen, Datenbank-Seiten und vollständige SQLite-Datenbanken aus Restblöcken in 18 von 24 Produktions-Platzierungen und 20 von 22 zugrunde liegenden Knoten auf vier Kontinenten wieder her.
Warum partielle Schreibvorgänge der Schlüssel-Vektor sind
Das Lesen einer nicht zugeordneten Region einer neuen Thin-Disk gibt Nullen zurück — dm-thin liefert Nullen, ohne einen physischen Block zuzuweisen. Das ist sicher. Der Exploit funktioniert, weil ein kleiner Schreibvorgang die Zuweisung eines recycelten 64-KiB-Blocks auslöst, ohne ihn vorher zu nullen.
Das Angriffsmuster:
- Erstellen Sie einen Container auf einem Workers-Paid-Konto.
- Öffnen Sie
/dev/vdc(die beschreibbare Root-Disk). - Identifizieren Sie 64-KiB-ausgerichtete Regionen, die dem ext4-Freibereich entsprechen.
- Schreiben Sie einen ausgerichteten 4-KiB-Block in jede Zielregion.
- Lesen Sie die vollständigen 64-KiB-Blöcke zurück.
- Untersuchen Sie nur die 60 KiB, die der Angreifer nicht überschrieben hat.
Schritt 4 ist der kritische Moment. Der 4-KiB-Schreibvorgang veranlasst dm-thin, einen physischen Block aus dem gemeinsamen Pool zuzuweisen. Da das Nullsetzen deaktiviert ist, können die verbleibenden 60 KiB Restdaten des vorherigen Besitzers dieses Blocks enthalten.
Was der Forscher tatsächlich validierte
Die Forscher verwendeten ext4-Verzeichnisblock-Prüfsummen (das metadata_csum-Feature), um ihre eigenen Testdateisystem-Blöcke von fremden Blöcken zu unterscheiden. Über sechs Produktions-Platzierungen:
- 5.614 testbare Verzeichnisblöcke untersucht
- 0 Blöcke dem eigenen Dateisystem der Forscher zugeordnet
- 2.700 verschiedene fremde Verzeichnis-Inodes per Prüfsummenanalyse identifiziert
Die wiederhergestellten Dateitypen waren kein Müll. Sie umfassten strukturell bedeutsame SQLite-Datenbanken — die Art von Daten, die im Game-Backend-Kontext Spieler-Save-States, Session-Tokens oder Inventardaten enthalten könnten.
Detektions-Runbook: Restdaten-Harvesting in Ihrer Infrastruktur erkennen
Wenn Sie containerisierte Game-Backends auf Multi-Tenant-Infrastruktur betreiben, benötigen Sie zwei Detektionsebenen: Konfigurations-Auditing und Laufzeit-I/O-Anomalieanalyse.
Schritt 1: Auditieren Sie Ihre dm-thin-Konfiguration
Führen Sie dieses Skript auf jedem Host aus, der Ihre Container betreibt:
#!/bin/bash
set -euo pipefail
echo "=== dm-thin Pool Configuration Audit ==="
echo ""
# Find all thin pool devices on this host
for pool in $(dmsetup status --target thin-pool 2>/dev/null | awk '{print $1}'); do
echo "Pool: $pool"
TABLE=$(dmsetup table "$pool")
echo " Full table: $TABLE"
if echo "$TABLE" | grep -q "skip_block_zeroing"; then
echo " ⚠️ WARNING: skip_block_zeroing is ENABLED"
echo " Recycled blocks may retain previous tenant data."
echo " Action: Remove skip_block_zeroing from pool table."
else
echo " ✅ Block zeroing is active (default dm-thin behavior)"
fi
echo ""
done
echo "=== Thin-block size check ==="
for pool in $(dmsetup status --target thin-pool 2>/dev/null | awk '{print $1}'); do
BLOCK_SECTORS=$(dmsetup table "$pool" | grep -oP '\d+ \d+ thin-pool' | awk '{print $1}')
BLOCK_KB=$((BLOCK_SECTORS * 512 / 1024))
echo " Pool $pool: block size = ${BLOCK_KB} KiB"
if [ "$BLOCK_KB" -le 64 ]; then
echo " ⚠️ Small block size amplifies residual data exposure."
echo " Larger block sizes reduce the ratio of leftover bytes per partial write."
fi
done
Wenn skip_block_zeroing irgendwo in Ihrer Pool-Konfiguration auftaucht, haben Sie dieselbe Schwachstellenklasse, die Cloudflare gepatcht hat. Beheben Sie es sofort — warten Sie nicht auf ein geplantes Wartungsfenster.
Schritt 2: Überwachen Sie die charakteristische I/O-Signatur
Der Exploit erzeugt eine erkennbare Signatur: kleine Schreibvorgänge, gefolgt von unverhältnismäßig großen Lesevorgängen. Ein 4-KiB-Schreibvorgang weist einen 64-KiB-Block zu; ein anschließender Lesevorgang ruft die vollen 64 KiB ab. Container-Telemetrie, bei der das Lesevolumen das Schreibvolumen um >10x übersteigt, ist verdächtig.
#!/usr/bin/env python3
"""
Detect anomalous write-then-read patterns characteristic of
cross-tenant residual data harvesting in thin-provisioned containers.
Usage: python detect_residual_harvesting.py io_events.jsonl
Input: JSONL file with one event per line:
{"container_id": "abc123", "operation": "write", "size_bytes": 4096}
"""
import json
import sys
from collections import defaultdict
ANOMALY_THRESHOLD = 10.0 # read_bytes : write_bytes ratio
def analyze_io_events(events_file):
with open(events_file) as f:
events = [json.loads(line) for line in f]
containers = defaultdict(
lambda: {"writes": 0, "reads": 0, "write_bytes": 0, "read_bytes": 0}
)
for event in events:
cid = event.get("container_id", "unknown")
op = event.get("operation")
size = event.get("size_bytes", 0)
if op == "write":
containers[cid]["writes"] += 1
containers[cid]["write_bytes"] += size
elif op == "read":
containers[cid]["reads"] += 1
containers[cid]["read_bytes"] += size
print("=" * 60)
print("CONTAINERS WITH ANOMALOUS READ/WRITE RATIOS")
print("=" * 60)
flagged = 0
for cid, stats in sorted(containers.items()):
if stats["write_bytes"] == 0:
continue
ratio = stats["read_bytes"] / stats["write_bytes"]
if ratio > ANOMALY_THRESHOLD:
flagged += 1
print(f"\n🚩 Container: {cid}")
print(f" Writes: {stats['writes']:,} ops | "
f"{stats['write_bytes']:,} bytes")
print(f" Reads: {stats['reads']:,} ops | "
f"{stats['read_bytes']:,} bytes")
print(f" Ratio: {ratio:.1f}x — investigate")
if flagged == 0:
print("\n✅ No anomalous containers detected.")
print(f"\nScanned {len(containers)} containers. Flagged: {flagged}")
return flagged
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python detect_residual_harvesting.py <io_events.jsonl>")
sys.exit(1)
sys.exit(1 if analyze_io_events(sys.argv[1]) > 0 else 0)
Beim Cloudflare-Vorfall erzeugte der Proof-of-Concept der Forscher genau diese Signatur. Das Sicherheitsteam von Cloudflare baute Detektionssignaturen aus dem PoC, wandte sie auf historische Disk-I/O-Telemetrie an und bestätigte, dass die einzige übereinstimmende Aktivität von den Forschern und Cloudflares eigenen Ingenieuren während der autorisierten Validierung stammte. Es wurde keine Ausnutzung durch Dritte festgestellt.
Die Lektion: Wenn Sie Container-I/O-Telemetrie sammeln, können Sie retrospektiv auditieren. Wenn Sie sie nicht sammeln, fliegen Sie blind.
Remediation-Runbook
Wenn Ihr Audit skip_block_zeroing oder eine ähnliche Offenlegung aufdeckt, finden Sie hier die Remediation-Sequenz, die Cloudflare ausgeführt hat — und die Reihenfolge ist wichtig.
Schritt 1: Block-Nullsetzen auf allen Pools wieder aktivieren
Entfernen Sie skip_block_zeroing aus Ihrer dm-thin-Pool-Konfiguration. Dies ist eine Laufzeitänderung am Thin-Pool-Gerät, schützt aber nur neue Zuweisungen. Blöcke, die bereits in laufenden Containern und gecachten Image-Snapshots zugeordnet sind, bleiben verwundbar.
# This is a simplified representation — actual dm-thin
# pool recreation commands vary by your orchestration layer.
# The core principle: restart the pool WITHOUT skip_block_zeroing.
dmsetup reload <pool-name> --table "0 <size> thin-pool <meta_dev> <data_dev> <data_block_size> 0"
dmsetup suspend <pool-name>
dmsetup resume <pool-name>
Cloudflare integrierte diesen Fix innerhalb von etwa 6 Stunden nach dem ersten Bericht. Die Forscher bestätigten unabhängig, dass der PoC nach dieser Änderung nicht mehr funktionierte.
Schritt 2: Alle laufenden Container-Disks ausmustern
Blöcke, die bereits in aktiven Thin-Devices zugeordnet sind, behalten ihren (nicht genullten) Inhalt. Der einzige Weg, Restdaten zu eliminieren, besteht darin, jede Container-Disk zu zerstören und neu zu erstellen.
Cloudflare leerte die Hosts außerhalb der Spitzenzeiten und startete die VMs auf jedem Host neu. Dies ist eine rollierende Operation — planen Sie sie für ein Game-Backend in Ihrem verkehrsärmsten Zeitfenster. Rechnen Sie mit einer kurzen Unterbrechung pro Host. Wenn Ihre Matchmaking-Schicht einen Graceful Handoff unterstützt, werden Spieler auf geleerten Hosts migriert statt getrennt.
Schritt 3: Gecachte Image-Snapshots löschen
Dieser Schritt wird leicht übersehen. Container-Orchestrierungsebenen cachen OCI-Image-Layer typischerweise als dm-thin-Snapshots für schnellen Container-Start. Diese gecachten Snapshots wurden vor dem Fix erstellt und können Restdaten in ungenutzten Regionen (einschließlich ext4-Freibereich) enthalten. Ein neuer Container, der einen gecachten Layer erbt, kann Restbytes aus /dev/vdc lesen, ohne jemals einen neuen Block zuzuweisen.
Cloudflare entfernte alle vor der Mitigation gecachten Snapshots in der betroffenen Flotte und schloss die Bereinigung 15 Tage nach dem ersten Bericht ab. Die gecachten Layer wurden mit genullten Zuweisungen neu erstellt.
Die Reihenfolge der Neuerstellung ist wichtig. Wenn Sie Caches löschen, bevor Sie das Nullsetzen aktivieren, haben Sie ungenullte Blöcke direkt zurück in frische Snapshots geschoben.
Schritt 4: Validieren Sie, dass der Fix hält
Führen Sie nach der Remediation das Detektionsskript mindestens 48 Stunden lang gegen neue I/O-Telemetrie aus. Vergleichen Sie die Schreib-/Lese-Verhältnisse mit Ihrer Baseline vor dem Patch. Das charakteristische Hochverhältnis-Muster sollte vollständig verschwinden.
Sie sollten die dm-thin-Tabelle auf jedem Host nach dem Neustart überprüfen:
# Confirm no host still has skip_block_zeroing
dmsetup table 2>/dev/null | grep -q "skip_block_zeroing" && \
echo "CRITICAL: Host $(hostname) still has skip_block_zeroing" || \
echo "OK: $(hostname) — block zeroing active"
Architektonische Prinzipien zur Vermeidung dieser Schwachstellenklasse
Der Cloudflare-Vorfall ist ein Beispiel für eine breitere Klasse von Multi-Tenant-Speicherisolationsfehlern. Hier sind die Prinzipien, die Ihr Game-Backend schützen — ob Sie Ihre eigenen Container verwalten oder sich auf eine Plattform verlassen.
Prinzip 1: Block-Nullsetzen in Multi-Tenant-Pools niemals deaktivieren
Das skip_block_zeroing-Flag existiert aus Performance-Gründen — das Nullsetzen von 64-KiB-Blöcken bei jeder Zuweisung erzeugt I/O-Overhead. Auf Single-Tenant-Hardware, auf der nur Ihr Code läuft, mag der Kompromiss akzeptabel sein. Auf gemeinsamer Infrastruktur, auf der Container über Tenants hinweg recycelt werden, ist es ein Sicherheitsdefekt.
Regel: Jeder dm-thin-Pool, der Blöcke an mehr als eine Tenant-Identität zuweist, muss Block-Nullsetzen aktiviert haben. Setzen Sie dies auf der Infrastructure-as-Code-Ebene durch, damit kein Host ohne diese Einstellung provisioniert werden kann.
Prinzip 2: Container-Disks als ephemer und gefährlich behandeln
Ihr Game-Backend sollte niemals davon ausgehen, dass die Disk eines Containers beim Start sauber ist. Selbst mit aktiviertem Block-Nullsetzen gibt es Randfälle mit Caching-Layern, Snapshot-Vererbung und Kernel-Bugs.
Schreiben Sie Ihren Container-Entrypoint so, dass das beschreibbare Volume beim Boot formatiert oder gelöscht wird:
#!/bin/bash
# Ensure a clean writable disk on container start
if [ -b /dev/vdc ]; then
# Overwrite with zeros (slow but thorough)
# For a game backend, the writable disk is typically small (1-4 GiB)
dd if=/dev/zero of=/dev/vdc bs=1M count=4096 status=progress
# Create a fresh filesystem
mkfs.ext4 -F /dev/vdc
mount /dev/vdc /workspace
fi
Das fügt dem Container-Start ein paar Sekunden hinzu. Für ein Game-Match, das 5–45 Minuten läuft, ist das akzeptabel. Für Sub-Sekunden-Cold-Start-Anforderungen verwenden Sie den schnelleren Ansatz: blkdiscard (das TRIM auslöst und je nach Storage-Backend Blöcke nullen kann) kombiniert mit mkfs.ext4 -F.
Prinzip 3: I/O-Verhältnisse als Sicherheitssignal überwachen
Die meisten Container-Monitoring-Lösungen konzentrieren sich auf CPU, Speicher und Netzwerk. Fügen Sie Disk-I/O zu Ihrer Sicherheitstelemetrie hinzu. Das Verhältnis von gelesenen zu geschriebenen Bytes ist ein starkes Signal für Restdaten-Harvesting-Angriffe — legitime Game-Workloads schreiben und lesen in ausgewogenen Mustern. Ein Container, der 4-KiB-Blöcke schreibt und dann 60+ KiB pro Region zurückliest, ist anomal.
Wenn Ihr Backend bereits Container-Lebenszyklus-Ereignisse protokolliert, können Sie I/O-Anomalien mit Tenant-Identität und Erstellungszeitstempeln korrelieren, um den Blast Radius eines potenziellen Vorfalls einzugrenzen. Für Spieleentwickler, die integrierte Crash-Reports und Benutzer-Logs wünschen, ohne diese Telemetrie-Infrastruktur selbst zu verwalten, bieten Plattformen wie horizOn diese Funktionen out of the box — das bedeutet, Sie verbringen weniger Zeit mit dem Bau von Infrastruktur und mehr Zeit mit Game-Logik.
Prinzip 4: Defense in Depth für Spielerdaten implementieren
Selbst wenn Ihre Container-Disk leakt, ist der Schaden begrenzt, wenn die Daten darauf verschlüsselt oder ohne Schlüssel bedeutungslos sind. Speichern Sie Spieler-Save-States, Session-Tokens und Inventardaten verschlüsselt at rest. Der Verschlüsselungsschlüssel sollte in einem Secrets Manager liegen, nicht auf der Container-Disk.
Dies ist dasselbe Prinzip, das wir in unserer Analyse der Star-Citizen-Datenpanne und der Architektur von Game-Backends, die Kompromittierungen überstehen behandelt haben — Defense in Depth bedeutet anzunehmen, dass jede einzelne Ebene irgendwann versagen wird.
Best-Practices-Checkliste
Auditieren Sie jede dm-thin-Pool-Konfiguration vor dem Deployment. Fügen Sie Ihrer Container-Orchestrierungs-Pipeline einen Pre-Flight-Check hinzu, der fehlschlägt, wenn
skip_block_zeroingvorhanden ist. Das ist ein Fünf-Zeilen-CI-Check, der eine ganze Schwachstellenklasse verhindert.Sammeln Sie Container-Disk-I/O-Telemetrie mit Tenant-Zuordnung. Sie können keine Ausnutzung erkennen, die Sie nicht beobachten. Protokollieren Sie Schreib-/Lesevolumen pro Container, korreliert mit Tenant-ID und Host-ID. Bewahren Sie mindestens 30 Tage für retrospektive Untersuchungen auf.
Nullen oder löschen Sie beschreibbare Container-Volumes beim Boot. Verlassen Sie sich nicht allein auf die Storage-Ebene. Ein frisches
mkfs.ext4beim Start fügt 2–5 GiB Schreib-Overhead hinzu, garantiert aber, dass keine Restdaten das Container-Recycling überleben.Leeren und recyceln Sie Container während Sicherheits-Patches, nicht nur danach. Das Aktivieren des Block-Nullsetzens schützt nur neue Zuweisungen. Bestehende Zuordnungen in laufenden Containern und gecachte Image-Snapshots müssen explizit gelöscht werden. Folgen Sie dem Fix immer mit einem flottenweiten Recycle.
Verschlüsseln Sie sensible Spielerdaten at rest mit externem Schlüsselmanagement. Wenn ein Restblock leakt, ist Chiffretext ohne Schlüssel nutzlos. Spieler-Save-States, die über horizOns Account-gebundenen Cloud Save verwaltet werden, verwenden revisionsbewusste Schreibvorgänge und clientseitige Konfliktauflösung — aber die zugrunde liegende Disk-Isolation bleibt Ihre Verantwortung, wenn Sie Container selbst hosten.
Zeitleiste: Wie Cloudflare reagierte
Zur Referenz: So schnell kann ein gut ausgestattetes Team auf eine kritische Infrastruktur-Schwachstelle reagieren:
| Zeit (UTC) | Aktion |
|---|---|
| 4. Sep, 15:26 | Forscher meldet über HackerOne |
| 4. Sep, 18:45 | Sicherheitsvorfall eröffnet, Produktions-Setup bestätigt |
| 4. Sep, 21:27 | Runtime-Fix integriert mit Reuse-Test |
| 4. Sep, 23:15 | Rollout beginnt |
| 7. Sep, 06:13 | Rollout abgeschlossen, Bereinigung beginnt |
| 14. Sep, 10:50 | Forscher bestätigt, dass PoC nicht mehr funktioniert |
| 19. Sep, 15:03 | Alle Pre-Mitigation-Cache-Snapshots gelöscht |
Das sind weniger als 3 Stunden vom Bericht bis zur bestätigten Grundursache und weniger als 8 Stunden bis zum integrierten Fix. Die vollständige Flottenbereinigung dauerte 15 Tage — normal für rollierende Operationen über eine globale Infrastruktur.
Die Geschwindigkeit ist entscheidend. Jede Stunde zwischen einem Schwachstellenbericht und einem Fix ist eine Stunde, in der Ausnutzung möglich ist. Wenn Sie Ihre eigene Container-Infrastruktur betreiben, bauen Sie das Runbook bevor Sie es brauchen.
Das Fazit
Die Cloudflare-Container-Sicherheitslücke war kein Zero-Day im kryptografischen Sinne. Es war ein bekannter Speicherkonfigurations-Kompromiss — Performance über Sicherheit — der für Multi-Tenant-Workloads ungeeignet war. Der Fix war eine einzelne Konfigurationsänderung plus ein Flotten-Recycle.
Wenn Sie Game-Backends auf gemeinsamer Container-Infrastruktur betreiben, auditieren Sie Ihre dm-thin-Pools noch heute. Führen Sie das Detektionsskript gegen Ihre I/O-Telemetrie aus. Und wenn Sie Container-Disk-Isolation, Flotten-Recycling und I/O-Telemetrie-Pipelines lieber nicht selbst verwalten möchten, übernimmt horizOn Authentifizierung, Crash-Reports, Cloud Save, Leaderboards und die anderen Backend-Dienste, die Ihr Spiel benötigt — damit Sie sich auf das Ausliefern Ihres Spiels konzentrieren können, statt um 3 Uhr morgens Storage-Sicherheit in der Produktion zu debuggen.
Quelle: So behob Cloudflare eine Cross-Tenant-Datenoffenlegungsschwachstelle in Containers