CVE-2026-104795 in OpenShift Container Platform
Zusammenfassung
von VulDB • 14.10.2026
Der insights-runtime-extractor wird als privilegiertes DaemonSet (`hostPID: true`) ausgeführt, das Container-Prozesse auf Runtime-Fingerabdrücke (Runtime Fingerprinting) scannt. Beim Scannen eines Containers führt der Koordinator Folgendes aus:
1. **fork()**t einen Kindprozess 2. Wechselt mit **setns()** in die PID- und Mount-Namespace des Zielcontainers 3. Wechselt mit **fchdir()** zurück zu einem verwaisten Dateideskriptor (FD), der auf das eigene Root-Dateisystem des Extractors zeigt (wo `/crio.sock` als hostPath gemountet ist) 4. Ruft **nur `seteuid(target_uid)`** auf — ändert die effektive UID, lässt jedoch die **reale UID und die gespeetzte Set-UID bei 0** und behält den zulässigen Capability-Satz (permitted capability set) 5. Die Schutzfunktion `check_no_privileged_perms()` überprüft nur den Satz der effektiven Capabilities (besteht also), und wird **vollständig übersprungen, wenn das Ziel als uid 0 ausgeführt wird** 6. Der Fingerprint `VersionExecutable` ruft `fpr_kind_executable` mit `process.command_line[0]` auf — dem argv[0] des Ziels, gelesen aus `/proc/<pid>/cmdline`, der **vollständig vom Workload kontrolliert** wird
7. `fpr_kind_executable` führt `exec.Command(executable, "--version").Output()` aus — wodurch das **eigene Binärprogramm der Workload** innerhalb des Mount-Namespace der Workload ausgeführt wird, während die reale UID (ruid) im Host-User-Namespace 0 ist
Der ausgeführte Prozess kann `setuid(0)` aufrufen, um vollständige Root-Rechte mit `CAP_SYS_ADMIN` wiederherzustellen, und erbt den verwaisten aktuellen Arbeitsverzeichnispfad (cwd), der in das Dateisystem des Extractor-Containers aufgelöst wird, welches die Host-CRI-O-Socket enthält.
## Betroffener Code
### 1. Unzureichender Abbau von Berechtigungen (Rust — Koordinator des Extractors)
**Datei:** `extractor/src/insights_runtime_extractor.rs`
```rust // Zeilen 139-146 — Nur seteuid wird reduziert, Guard für Root-Container übersprungen join_process_namespaces(process.pid)?; change_dir(current_dir); switch_user(*process.uid)?; if *process.uid != 0 {
perms::check_no_privileged_perms() .expect("Must not have privileged permissions to run fingerprints"); } fingerprint::run_fingerprints(&config, out_dir, &process); ```
```rust // Zeilen 182-185 — Nur effektive UID wird geändert; ruid/suid bleiben bei 0 fn switch_user(uid: u32) -> Result<(), ScannerError> {
debug!("... Becoming user: {uid}");
seteuid(uid.into()).map_err(|e| ScannerError::Errno(e)) } ```
### 2. Ausführung von nicht vertrauenswürdigen Binärdateien (Go — Fingerprinting)
**Datei:** `fingerprints/pkg/utils/file.go`
```go // Zeilen 75-80 — führt einen Pfad aus, der wörtlich aus argv[0] der Workload übernommen wurde
func GetExecutableVersionOutput(executable string) (string, error) {
out, err := exec.Command(executable, "--version").Output() if err != nil {
return "", err } return string(out), nil } ```
### 3. Von der Workload kontrollierter Pfad wird weitergeleitet (Rust — version_executable)
**Datei:** `version_executable.rs`
```rust // Zeilen 29-34 — von der Workload kontrolliertes command_line[0] wird an das Fingerprinting übergeben
return Some(vec![
fpr_kind_executable, out_dir.to_string(), String::from(&process.command_line[0]),
String::from(&version_executable.runtime_kind_name), ]); ```
## Angriffsmuster
**Angreifer:** Jeder Tenant, der einen Pod planen kann (unprivilegiert, beliebiger Namespace) **Opfer:** Der Knoten, auf dem das DaemonSet insights-runtime-extractor ausgeführt wird **Auslöser:** Automatisch — Das DaemonScannt alle Container-Prozesse auf dem Knoten
**Angriffsablauf:**
1. Der Angreifer erstellt einen Pod, dessen Prozessname mit einem konfigurierten Muster für versionierte ausführbare Dateien übereinstimmt (`node`, `python`, `python3`, `MainThread` oder enthält `java`) 2. Das Hauptbinärprogramm des Pods (am Pfad entsprechend argv[0]) ist eine bösartige ausführbare Datei, die `setuid(0)` aufruft, um Root-Rechte wiederherzustellen
3. Wenn der insights-Operator einen Scan auslöst, fork()t das DaemonSet den Prozess, betritt den Mount-Namespace des Angreifers und führt das Binärprogramm des Angreifers mit ruid=0 aus 4. Der Code des Angreifers gewinnt volle Root-Rechte mit `CAP_SYS_ADMIN` im Host-User-Namespace zurück 5. Das vererbte cwd enthält die Host-CRI-O-Socket → vollständige Kompromittierung des Knotens
If you want to get best quality of vulnerability data, you may have to visit VulDB.