Неаутентифицированное раскрытие внутреннего пути к папке, электронной почты клиента и политики загрузки для клиентских порталов FileRise Pro через /api/pro/portals/get.php
get.php)public/api/pro/portals/get.php возвращает полную внутреннюю запись клиентского портала FileRise Pro — включая путь к внутренней папке хранилища, контактный email клиента и полную политику загрузки — любому неаутентифицированному вызывающему, который знает или может угадать slug портала.
Каждый соседний эндпоинт в том же каталоге (list.php, save.php, listEntries.php, submitForm.php, submissions.php) вызывает fr_pro_guard_auth(...) перед выполнением; get.php — единственное исключение. Приложение уже содержит намеренно ограниченный публичный эндпоинт для того же slug (publicMeta.php), который возвращает только поля брендинга — что доказывает, что эти данные не предназначены для раскрытия до аутентификации.
public/api/pro/portals/get.phpProPortalsApiService::getPortal() → PortalController::getPortalBySlug() (src/FileRise/Http/Controllers/PortalController.php:60)error311/FileRise @ тег v3.23.1 (собрано из собственного Dockerfile проекта, свежая первоначальная настройка)get.php выполняется без шлюза аутентификации:
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) { ... }
В этом файле нигде нет вызова fr_pro_guard_auth() — в отличие от list.php / save.php / listEntries.php, которые все вызывают его первым.
getPortal() возвращает ту же запись, которую администратор видит на экране редактирования портала, включая чувствительные поля:
return [
'slug' => ..., 'label' => ..., 'folder' => $folder, 'clientEmail' => $clientEmail,
'sourceId' => $sourceId, 'uploadOnly' => ..., 'allowDownload' => ...,
'uploadMaxSizeMb' => ..., 'uploadExtWhitelist' => ..., 'uploadMaxPerDay' => ...,
'allowSubfolders' => ..., 'formDefaults' => ..., 'canUpload' => $canUpload, ...
];
Для сравнения, publicMeta.php — эндпоинт, который фронтенд (public/js/portal.js) фактически вызывает для анонимной страницы входа в портал — загружается из отдельной минимальной проекции portals.json через PortalPublicMetaService::getPublicPortalMeta() и возвращает только поля брендинга:
['slug', 'label', 'title', 'introText', 'brandColor', 'footerText', 'logoFile', 'logoUrl']
О предусловии: атакующему нужно только знать или угадать slug портала — короткую, удобочитаемую метку, выбранную администратором и используемую непосредственно в публичной ссылке портала (/portal/<slug>), а не секрет с высокой энтропией. Slug'и регулярно передаются клиентам по email или ссылкой, поэтому это реалистично достижимо для любого, кто когда-либо получал ссылку на портал.
Проверено вживую на error311/FileRise @ v3.23.1, с нулевыми cookies/сессией в уязвимом запросе.
Примечание о тестировании: FileRise Pro (лицензированный пакет
ProPortals.php) — это отдельный платный аддон, не включённый в этот репозиторий. Приведённый ниже PoC активирует режим Pro с минимальным локальным тестовым заглушком, реализующим только интерфейсlistPortals(), который код OSS уже вызывает (PortalController.php:60→new ProPortals(FR_PRO_BUNDLE_DIR)→->listPortals()). Это воспроизводит в точности тот путь кода на стороне OSS, который выполняется в каждом реальном лицензированном экземпляре; проприетарный код не использовался и не требовался.
1. Сборка образа:
git clone https://github.com/error311/FileRise.git
cd FileRise
docker build -t filerise-local -f Dockerfile .
2. Минимальный локальный заглушек для проверки кода гейтинга Pro на стороне OSS (не проприетарного пакета — ровно столько, чтобы удовлетворить 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