
Pfad-Traversal (Tar Slip) in Cornac über _extract_archive (CVE-2026-43637)
Schweregrad: Hoch, CVSS 4.0 8.8, Kritisch, CVSS 3.1 9.1(zugewiesen von VulnCheck, dem CNA)
Vektor (v4.0): CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N
Vektor (v4.0): CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H
Betroffen: cornac < 2.6.0
Behoben in: 2.6.0
CWE: CWE-22 (Unzureichende Einschränkung eines Pfadnamens auf ein eingeschränktes Verzeichnis, 'Path Traversal')
Gemeldet von: Rahul Karne und Bharath Kumar Reddy Janumpally
CNA: VulnCheck
Veröffentlicht: 15. Juli 2026
Cornacs Dataset-Loader laden Archive automatisch herunter und entpacken sie, und das Entpackprogramm vertraute jedem Pfad innerhalb des Archivs.
Cornac ist ein Machine-Learning-Framework für Empfehlungssysteme. Seine eingebauten Dataset-Loader laden Archive über das Netzwerk herunter und extrahieren sie ohne jeden Bestätigungsschritt. Die Extraktionsroutine _extract_archive() in cornac/utils/download.py ruft archive.extractall() ohne Validierung der Mitgliedspfade auf. Ein TAR-Archiv, dessen Mitglieder ../-Sequenzen, absolute Pfade oder Symlink- und Hardlink-Einträge enthalten, schreibt daher Dateien an beliebige Stellen im Dateisystem, überall dorthin, wo der laufende Prozess schreiben kann, vollständig außerhalb des vorgesehenen Cache-Verzeichnisses. Mehrere Loader laden über unverschlüsseltes HTTP, sodass ein im Netzwerk positionierter Angreifer ein bösartiges Archiv während der Übertragung unterschieben und es in dem Moment extrahieren lassen kann, in dem ein Loader aufgerufen wird.
Beliebiges Dateischreiben mit vollständig vom Angreifer kontrolliertem Pfad und Inhalt, begrenzt nur durch die Berechtigungen des Prozesses, der Cornac ausführt. Dies ist ein Schreib-Primitiv, kein Lese-Primitiv, daher gibt es keine direkte Offenlegung, aber beliebiges Dateischreiben ist ein etablierter Weg zur Codeausführung und zur Denial-of-Service:
.py-Datei in site-packages, einer Shell-Startdatei oder eines Eintrags für geplante Aufgaben führt dazu, dass Angreifercode beim nächsten Import, Shell-Start oder Job ausgeführt wird.Wer ist betroffen: Jede Nutzung von cornac < 2.6.0, die einen Dataset-Loader aufruft, der ein Archiv herunterlädt und extrahiert, das der Angreifer kontrollieren oder abfangen kann. Da Loader automatisch extrahieren, ist kein Schritt über den normalen Loader-Aufruf hinaus erforderlich.
Wer ist nicht betroffen:
2.6.0 oder neuer, wo die Extraktion jeden Mitgliedspfad validiert.| Kennzahl | Wert | Quelle |
|---|---|---|
| Downloads, gesamt | 4.1M | pepy.tech/projects/cornac |
| Downloads, letzte 30 Tage | 67.8K | pepy.tech |
| Typische Verwendung | Forschung und Lehre zu Empfehlungssystemen; Dataset-Loader holen automatisch über das Netzwerk, einige über unverschlüsseltes HTTP | dem Framework inhärent |
_extract_archive() in cornac/utils/download.py extrahiert sowohl ZIP- als auch TAR-Eingaben über einen einzigen Codepfad und ruft für den TAR-Fall extractall() ohne Pfadprüfung auf:
# cornac/utils/download.py — _extract_archive(), lines 50-71 (v2.3.5)
def _extract_archive(file_path, extract_path="."):
"""Extracts an archive."""
for archive_type in ["zip", "tar"]:
if archive_type == "zip":
open_fn = zipfile.ZipFile
is_match_fn = zipfile.is_zipfile
elif archive_type == "tar":
open_fn = tarfile.open
is_match_fn = tarfile.is_tarfile
if is_match_fn(file_path):
with open_fn(file_path) as archive:
try:
archive.extractall(extract_path) # <-- no member-path validation
except (tarfile.TarError, RuntimeError, KeyboardInterrupt):
if os.path.exists(extract_path):
if os.path.isfile(extract_path):
os.remove(extract_path)
else:
shutil.rmtree(extract_path)
raise
Nichts schränkt die Mitgliedsnamen ein, daher löst ein Mitglied namens ../../somewhere/file außerhalb von extract_path auf und wird dorthin geschrieben.
Die Aufrufkette ist ab einem Loader-Aufruf vollständig automatisch:
cornac.datasets.<name>.load_feedback()
-> cornac.utils.download.cache() # downloads via urllib.request.urlretrieve
-> cornac.utils.download._extract_archive()
-> tarfile.extractall() # writes attacker-named paths
Pythons zipfile bereinigt ../-Sequenzen während der Extraktion, daher ist der ZIP-Zweig derselben Funktion auf modernem Python nicht ausnutzbar. Pythons tarfile bereinigt Mitgliedspfade nicht (vor 3.12, und danach nur mit einem expliziten Filter). Da Cornac beide Formate über denselben extractall()-Aufruf verarbeitet, ist der ZIP-Zweig sicher und der TAR-Zweig über dieselben Zeilen vollständig ausnutzbar. Die Asymmetrie ist genau deshalb leicht zu übersehen, weil der Code über beide Formate hinweg einheitlich aussieht.
Cornac lädt mit urllib.request.urlretrieve() herunter, ohne Zertifikats-Pinning und ohne Archiv-Integritätsprüfung, und mehrere eingebaute Loader verwenden http://-URLs. Ein im Netzwerk positionierter Angreifer kann den unverschlüsselten HTTP-Download abfangen und ein bösartiges TAR zurückgeben, ohne den Upstream-Server zu kompromittieren. Deshalb ist der Vektor remote und erfordert keine Benutzerinteraktion über den Loader-Aufruf hinaus.
Ein Angreifer benötigt:
< 2.6.0 verwendet.Es sind keine Privilegien auf dem Ziel und keine Benutzerinteraktion über den Loader-Aufruf hinaus erforderlich.
Das Folgende wurde gegen das echte, unveränderte Paket ausgeführt, indem Cornacs eigene _extract_archive direkt aufgerufen wurde.
Verwundbare Version (2.3.5). Ein TAR, das ein ../../-Mitglied enthält, wird in ein Cache-Verzeichnis extrahiert; das Mitglied landet außerhalb dieses Verzeichnisses und überschreibt eine Datei, die zu einer separaten Anwendung gehört:
import cornac.utils.download as d
import io, os, tarfile, tempfile
base = tempfile.mkdtemp()
cache = os.path.join(base, "cornac_scope", "cache"); os.makedirs(cache)
victim = os.path.join(base, "victim_scope", "webapp"); os.makedirs(victim)
open(os.path.join(victim, "app.py"), "w").write("def run():\n return 'healthy'\n")