Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Einreichen
ToolsExploitsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

FeedsKontaktDatenschutz© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
ZigRaceExploit — CVE-2021-25741 POC in Zig | Kitploit
Tools/GitHubGitHub/glutenfree69/zigraceexploit
Container-SicherheitSchwachstellenanalyseExploitationCloud-SicherheitLernen & BildungBinary-Exploitation
GitHubglutenfree69/zigraceexploit

ZigRaceExploit

CVE-2021-25741 POC in Zig

Repository anzeigen
28vor 7 MonatenNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

CVE-2021-25741 — TOCTOU-Symlink-Race-Exploit in Zig

Bildungs-Exploit der TOCTOU-Race-Condition (Time Of Check, Time Of Use), die die Ursache von CVE-2021-25741 ist, einer Schwachstelle im Kubernetes-Kubelet.

Das Projekt demonstriert lokal (ohne K8s-Cluster), wie ein gleichzeitiger Symlink-Swap eine Pfadprüfung zwischen lstat() und open() umgehen kann, und beweist, dass openat2() mit den Flags RESOLVE_* die Lösung ist.

Inhaltsverzeichnis

  • Das Problem
  • Wie der Exploit funktioniert
  • Code-Architektur
  • Voraussetzungen
  • Build
  • Verwendung
    • Lokaler Test mit Docker
    • Vergleich openat vs openat2
    • Mit der TOCTOU-Verzögerung spielen
    • Test auf einem echten Kubernetes-Knoten
  • Erwartete Ergebnisse
  • Code-Erklärung
  • Verwendete Linux-Syscalls
  • Debugging mit strace
  • Referenzen

Das Problem

Pfadauflösung unter Linux

Wenn der Kernel einen Pfad wie /a/b/c/file auflöst, tut er dies Komponente für Komponente. Bei jedem Schritt, wenn die Komponente ein Symlink ist, folgt der Kernel ihm automatisch. Diese Auflösung ist nicht atomar – das Dateisystem kann sich zwischen zwei Schritten ändern.

TOCTOU im Kubelet

Der Kubernetes-Kubelet verwendete genau dieses anfällige Muster:

1. CHECK : lstat(subPath) → "c'est un répertoire, c'est safe"
     ↕ FENÊTRE DE RACE — un process dans le container swap le répertoire pour un symlink
2. USE   : mount(subPath) → suit le symlink, monte le filesystem host

Zwischen der Überprüfung und dem Mount konnte ein bösartiger Prozess im Container das subPath durch einen Symlink auf / des Hosts ersetzen und so vollständigen Zugriff auf das Host-Dateisystem erhalten.

Der Fix: openat2(2)

Der Syscall openat2 (Kernel 5.6+) löst den Pfad und öffnet die Datei atomar, mit Einschränkungen:

FlagWirkung
RESOLVE_NO_SYMLINKSWeigert sich, Symlinks zu folgen → gibt ELOOP zurück
RESOLVE_BENEATHWeigert sich, über das Basisverzeichnis hinauszugehen → gibt EXDEV zurück
RESOLVE_IN_ROOTBehandelt den dirfd als Wurzel des Dateisystems
RESOLVE_NO_XDEVWeigert sich, Mountpunkte zu durchqueren

Mit openat2 gibt es kein TOCTOU-Fenster: Wenn während der Auflösung ein Symlink erscheint, schlägt der Syscall sofort fehl.


Wie der Exploit funktioniert

Zwei Threads arbeiten zusammen, um das TOCTOU-Fenster auszunutzen:

workdir/
├── legit_dir/
│   └── secret.txt  → contient "LEGIT"
├── symlink_target/
│   └── secret.txt  → contient "PWNED"
└── target/          → swappé entre vrai dir et symlink

Racer-Thread

Läuft in einer ultraschnellen Schleife und tauscht target/ atomar zwischen zwei Zuständen über renameat2(RENAME_EXCHANGE) aus:

  • Zustand A: target/ ist ein echtes Verzeichnis (enthält secret.txt = "LEGIT")
  • Zustand B: target/ ist ein Symlink auf symlink_target/ (enthält secret.txt = "PWNED")

Ein einziger Syscall pro Tausch = maximales Race-Fenster.

Victim-Thread (simuliert den Kubelet)

Reproduziert das anfällige Muster des Kubelet:

  1. fstatat("target", AT_SYMLINK_NOFOLLOW) — überprüft, dass es ein Verzeichnis ist
  2. Optionale Pause (simuliert die Latenz des Kubelet zwischen Check und Use)
  3. openat(dirfd, "target/secret.txt", O_RDONLY) — öffnet die Datei
  4. read() — liest den Inhalt
  5. Wenn Inhalt == "PWNED" → Race Win (dem Symlink wurde gefolgt)
  6. Wenn Inhalt == "LEGIT" → Race Loss (es war das echte Verzeichnis)

Im geschützten Modus (--use-openat2) verwendet Schritt 3 openat2 mit RESOLVE_NO_SYMLINKS | RESOLVE_BENEATH. Wenn ein Symlink vorhanden ist, gibt der Kernel ELOOP zurück, anstatt ihm zu folgen.


Code-Architektur

src/
├── main.zig       # Einstiegspunkt: parst CLI, erstellt Shared State, startet die
│                  # Threads, misst die Zeit, zeigt Ergebnisse an
│
├── racer.zig      # Racer-Thread: bereitet den Anfangszustand vor (target → symlink),
│                  # schleift dann über renameat2(RENAME_EXCHANGE), um target/ und
│                  # legit_dir/ kontinuierlich zu tauschen
│
├── victim.zig     # Victim-Thread: schleift N Iterationen von fstatat → delay →
│                  # openat/openat2 → read → vergleicht "LEGIT" vs "PWNED"
│
├── setup.zig      # Erstellt die Testverzeichnisstruktur: workdir/, legit_dir/,
│                  # symlink_target/, target/ mit den Sentinel-Dateien
│
├── syscalls.zig   # Konstanten und Wrapper für die rohen Syscalls:
│                  # - open_how-Struktur (Kernel-UAPI, 3×u64)
│                  # - RESOLVE_*-Flags
│                  # - RENAME_EXCHANGE (= 2)
│                  # - openat2() über linux.syscall4(.openat2, ...)
│                  # - rename_exchange() über linux.renameat2()
│                  # - Helfer: is_err(), to_errno(), to_fd()
│
└── stats.zig      # Lock-freie atomare Zähler (std.atomic.Value(u64)) für Wins,
                   # Losses, Errors, ELOOP + formatierte Ausgabe

Alle Syscalls werden direkt über std.os.linux.* aufgerufen (keine Wrapper std.fs oder std.posix). Der einzige Syscall ohne Wrapper in der Zig 0.15 Standardbibliothek ist openat2, den wir über linux.syscall4(.openat2, ...) mit einer manuell aus den UAPI-Headern des Kernels definierten open_how-Struktur aufrufen.


Voraussetzungen

WerkzeugVersionGrund
Zig0.15.xCompiler + Cross-Kompilierung
DockerbeliebigZum Ausführen des Linux-Binärs auf dem Mac
colimabeliebigDocker-Laufzeit auf macOS (oder Docker Desktop)

Das Binär wird statisch kompiliert (musl libc) und läuft auf jedem Linux ohne Abhängigkeiten.

Mindestkernel:

  • 3.15+ für renameat2(RENAME_EXCHANGE)
  • 5.6+ für openat2 mit RESOLVE_* (nur für --use-openat2 erforderlich)

Docker Desktop und colima auf dem Mac verwenden einen Kernel 6.x – alles wird unterstützt.


Build

Cross-Kompilierung Mac → Linux

# ARM64 (Mac M1/M2/M3 → Docker colima / EC2 ARM)
zig build -Dtarget=aarch64-linux-musl -Doptimize=ReleaseSafe

# x86_64 (für x86-Knoten)
zig build -Dtarget=x86_64-linux-musl -Doptimize=ReleaseSafe

Das Binär befindet sich in zig-out/bin/race-exploit.

$ file zig-out/bin/race-exploit
ELF 64-bit LSB executable, ARM aarch64, statically linked

Verwendung

Lokaler Test mit Docker

# Docker-Laufzeit starten (falls macOS)
colima start

# Exploit ausführen (Standard: anfälliger Modus)
docker run --rm -v $(pwd)/zig-out/bin:/app alpine /app/race-exploit --iterations 10000

Vergleich openat vs openat2

# Anfälliger Modus (openat) — die Race funktioniert
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 10000

# Geschützter Modus (openat2) — die Race wird blockiert
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 10000 --use-openat2

Oder mit dem Skript, das beide ausführt:

./scripts/run_in_docker.sh --iterations 10000

Mit der TOCTOU-Verzögerung spielen

Der Parameter --delay-us fügt eine Verzögerung zwischen lstat (Check) und openat (Use) hinzu. Je länger die Verzögerung, desto größer das TOCTOU-Fenster und desto höher die Win-Rate:

# Ohne Verzögerung — Win-Rate ~25%
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 50000

# 10µs — Win-Rate ~35%
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 50000 --delay-us 10
Tool herunterladen