Divulgazione non autenticata del percorso della cartella interna, dell'email del client e della policy di upload per i portali client di FileRise Pro tramite /api/pro/portals/get.php
get.php)public/api/pro/portals/get.php restituisce il record interno completo di un portale client di FileRise Pro — incluso il percorso della sua cartella di archiviazione interna, l'email di contatto del cliente e la sua policy di upload completa — a qualsiasi chiamante non autenticato che conosca o riesca a indovinare lo slug del portale.
Ogni endpoint affine nella stessa directory (list.php, save.php, listEntries.php, submitForm.php, submissions.php) invoca fr_pro_guard_auth(...) prima dell'esecuzione; get.php è l'unica eccezione. L'applicazione include già un endpoint pubblico deliberatamente curato per lo stesso slug (publicMeta.php) che restituisce solo campi di branding — a dimostrazione che questi dati non sono destinati ad essere esposti prima dell'autenticazione.
public/api/pro/portals/get.phpProPortalsApiService::getPortal() → PortalController::getPortalBySlug() (src/FileRise/Http/Controllers/PortalController.php:60)error311/FileRise @ tag v3.23.1 (compilato dal Dockerfile del progetto stesso, configurazione iniziale al primo avvio)get.php viene eseguito senza alcun controllo di autenticazione:
require_once __DIR__ . '/../_common.php'; require_once PROJECT_ROOT . '/src/FileRise/Domain/ProPortalsApiService.php';
try { $slug = isset($_GET['slug']) ? (string)$_GET['slug'] : ''; fr_pro_emit_result(\FileRise\Domain\ProPortalsApiService::getPortal($slug)); } catch (Throwable $e) { ... }
Non c'è alcuna chiamata a fr_pro_guard_auth() in questo file — al contrario di list.php / save.php / listEntries.php, che la invocano tutti per primi.
getPortal() restituisce lo stesso record che un amministratore vede nella schermata modifica portale, inclusi campi sensibili:
return [
'slug' => ..., 'label' => ..., 'folder' => $folder, 'clientEmail' => $clientEmail,
'sourceId' => $sourceId, 'uploadOnly' => ..., 'allowDownload' => ...,
'uploadMaxSizeMb' => ..., 'uploadExtWhitelist' => ..., 'uploadMaxPerDay' => ...,
'allowSubfolders' => ..., 'formDefaults' => ..., 'canUpload' => $canUpload, ...
];
Per contrasto, publicMeta.php — l'endpoint che il front-end (public/js/portal.js) chiama effettivamente per la pagina di atterraggio anonima del portale — carica da una proiezione separata e minimale di portals.json tramite PortalPublicMetaService::getPublicPortalMeta() e restituisce solo campi di branding:
['slug', 'label', 'title', 'introText', 'brandColor', 'footerText', 'logoFile', 'logoUrl']
Sulla precondizione: l'attaccante deve solo conoscere o indovinare uno slug di portale — un'etichetta breve, leggibile, scelta dall'amministratore, usata direttamente nell'URL condivisibile del portale (/portal/<slug>), non un segreto ad alta entropia. Gli slug vengono abitualmente condivisi con i clienti via email o link, quindi questo è realisticamente raggiungibile da chiunque abbia mai ricevuto un link al portale.
Verificato dal vivo su error311/FileRise @ v3.23.1, con zero cookie/sessione sulla richiesta vulnerabile.
Nota sui test: FileRise Pro (il bundle con licenza
ProPortals.php) è un add-on a pagamento separato non incluso in questo repository. Il PoC seguente attiva la modalità Pro con uno stub di test locale minimale che implementa solo l'interfaccialistPortals()che il codice OSS già chiama (PortalController.php:60→new ProPortals(FR_PRO_BUNDLE_DIR)→->listPortals()). Questo riproduce esattamente il percorso di codice lato OSS che ogni istanza realmente licenziata esegue; nessun codice proprietario è stato usato o richiesto.
1. Compilare l'immagine:
git clone https://github.com/error311/FileRise.git
cd FileRise
docker build -t filerise-local -f Dockerfile .
2. Stub locale minimale per esercitare il codice di gating Pro lato OSS (non il bundle proprietario — solo quanto basta per soddisfare FR_PRO_ACTIVE / ProPortals::listPortals()):
mkdir -p data/users/pro
cat > data/users/pro/bootstrap_pro.php <<'PHP'
<?php
define('FR_PRO_ACTIVE', true);
PHP
cat > data/users/pro/ProPortals.php <<'PHP'
<?php
class ProPortals {
public function __construct(string $dir) {}
public function listPortals(): array {
return ['client-portal' => [
'label' => 'Client Portal',
'folder' => 'confidential/client-acme-contracts',
'clientEmail' => '[email protected]',
'uploadOnly' => true, 'allowDownload' => false,
'uploadMaxSizeMb' => 25, 'uploadExtWhitelist' => 'pdf,docx', 'uploadMaxPerDay' => 10,
]];
}
}
PHP