
Stirling-PDF v3.2.0 — высокий уровень — удалённая — IDOR — перечисление корня хранилища processing-folder и перезапись PDF-файлов других пользователей
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · stirling-storage-folder-idor
Stirling-PDF 3.2.0 — Stirling Tools
Класс: IDOR. Доступ: удалённо, ROLE_USER.
POST /api/v1/processing-folders не имеет @PreAuthorize помимо требования авторизованного пользователя. FolderAccessGuard подразумевает весь локальный basePath хранилища (./storage), даже когда policies.allowedFolderRoots пуст. LocalStorageProvider записывает {ownerId}/{uuid}_{filename}. Пользователь с ROLE_USER может указать directory: "storage/<victimUserId>", получить список PDF-файлов этого владельца и перезаписать их на месте с replace=true.
Прямой /api/v1/storage/files/{id} пропатчен (findByIdAndOwner). GET файла жертвы от имени другого пользователя в лаборатории возвращает 403. Остаётся неявный корень processing-folder, а не CRUD API хранилища.
Пользователь с ROLE_USER получает список имён сохранённых PDF-файлов другого пользователя и перезаписывает эти блобы. Скачивание жертвой возвращает подменённый файл.
| ID | CVE пока нет |
| CWE | CWE-639 / CWE-73 |
| CVSS | 7.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N |
| Продукт | Stirling-PDF |
| Затронуто | v3.2.0 (a7e593728dd9aa81e75318a949789d3c2e9ee754), образ stirlingtools/stirling-pdf:3.2.0-fat, login-on, storage.enabled=true |
| Аутентификация | ROLE_USER в команде по умолчанию |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1:18154 |
Продолжение серии Stirling-PDF v3.2.0 leftovers. Смежный материал: stirling-h2-import-leftover (H2 CREATE OR REPLACE ALIAS). Этот пакет посвящён IDOR в хранилище.
Любой авторизованный пользователь ROLE_USER при login-on proprietary/fat с включённым локальным хранилищем (по умолчанию в шаблоне). Официальный compose использует login-off плюс DISABLE_ADDITIONAL_FEATURES=true; при таком развёртывании /api/v1/storage возвращает 403, а этот контроллер отсутствует. Развёртывание HIT — это шаблон login-on плюс fat.
Таблица привилегий (исчерпывающая). Тот же уровень, что и Signal-Server Dropwizard admin.
| Ось | Метод + путь | Даёт | Происхождение |
|---|---|---|---|
| Разведка | ROLE_USER GET /api/v1/user/users | Числовой userId для admin, другого пользователя и STIRLING-PDF-BACKEND-API-USER | Подтверждено в лаборатории |
| Разведка | POST /api/v1/processing-folders {directory:"storage/1"}, затем GET /api/v1/processing-folders/{id}/files | Имя, размер, mtime сохранённого PDF-файла admin (5341de35-8729-49cf-92d0-c8284e05bfc5_victim-lab.pdf) | Подтверждено в лаборатории |
| Целостность | POST /api/v1/processing-folders/{id}/sweep с /api/v1/security/add-watermark, затем Range GET /api/v1/storage/files/1/download от имени жертвы | Перезапись на месте. Ответ жертвы 206 / 3450 байт содержит STIRLING-STORAGE-FOLDER-IDOR-WITNESS | Подтверждено в лаборатории (replace=true) |
| Конфиденциальность | тот же sweep плюс командный outputId / исходящий шаг, который атакующий может прочитать | Копия байтов PDF-файла жертвы | Предположительно. Не требуется для перечисления/перезаписи |
Остаётся закрытым на этой фиксации
| Ось | Метод + путь | Почему закрыто |
|---|---|---|
| Разведка | GET /api/v1/storage/files/{id} для нерасшаренного файла | 403 Access denied. Доказано в лаборатории на файле с id 1 |
| Разведка | неаутентифицированный /api/v1/storage/** | 403, когда вход отключён (ensureStorageEnabled) |
| Целостность | CRUD папок по UUID между владельцами | findByIdAndOwner |
| Целостность | LocalStorageProvider .. / абсолютный ключ | Path.startsWith(basePath) после нормализации |
| Целостность | ultra-lite образ | проприетарный контроллер папок обработки отсутствует |
GET /api/v1/user/users со списком STIRLING-PDF-BACKEND-API-USER — это дополнительный IOC. Я не оформлял его как отдельный leftover. Скачивание жертвы без Range даёт 500 после перезаписи, потому что FileStorageController по-прежнему сообщает исходный sizeBytes из БД. Range работает. Текст водяного знака — Identity-H в FlateDecode; оракул распаковывает потоки.
В 3.x появились локальное хранилище и папки обработки. CRUD хранилища ограничен владельцем. FolderAccessGuard.serverStorageRoots возвращает Path.of(basePath) для всего дерева, а не basePath/{currentUserId}. storage/<victimId> проходит startsWith для этого подразумеваемого корня.
Лаборатория создаёт peeruser с ROLE_USER, загружает PDF жертвы от имени администратора, доказывает 403 при прямом GET хранилища, направляет папку обработки на storage/1, получает имя с префиксом UUID, подбирает водяной знак, а затем скачивает через Range от имени жертвы.
Неверные пути: считать прямой IDOR хранилища всё ещё открытым (403); считать официальный compose с отключённым входом за HIT-развёртывание (хранилище 403, контроллер отсутствует); заявлять о читаемой атакующим побайтовой копии без outputId.
python3 stirling-storage-folder-idor-Abraxas-Labs.py
или
cd lab
./run.sh
Цель — только http://127.0.0.1:18154. Образ stirlingtools/stirling-pdf:3.2.0-fat. Требуется Docker. Вход включён. Лаунчер выполняет chdir в lab/ и запускает run.sh.
IOC direct-storage-get http=403
IOC processing-folders-save directory=storage/1 http=202
IOC listed-peer ... victim-lab.pdf
IOC victim-download attempt=1 http=206 bytes=3450 witness=1
SUCCESS stirling-storage-folder-idor direct-storage-403=yes listed-peer-pdf=yes download-has-witness=yes STIRLING-STORAGE-FOLDER-IDOR-WITNESS
Ограничьте подразумеваемый корень хранилища до basePath/{currentUserId} для диска directory на многопользовательских серверах. Отклоняйте storage/<otherId> так же, как findByIdAndOwner уже отклоняет чужие идентификаторы файлов. Пересчитывайте объявленный sizeBytes после перезаписи на месте.
Повторно запустите loopback-клиент против исправленной сборки: directory=storage/1 от имени peeruser должен возвращать 403, а список peer-пользователей должен оставаться пустым.
До тех пор не считайте, что «я исправил /api/v1/storage/files/{id}» означает «я ограничил каждый API, принимающий путь к диску в ./storage».