
Уведомление: CVE-2026-38360 — обход пути (CWE-22) в dash-uploader (Python/PyPI)
Неаутентифицированный обход пути в fohrloop/dash-uploader (Python, PyPI), позволяющий произвольную запись файлов и приводящий, помимо прочего, к удалённому выполнению кода (RCE), перезаписи исходного кода приложения, хранимому XSS и установке постоянного бэкдора.
Репозиторий был архивирован 2025-07-19, активного мейнтейнера нет. Все опубликованные версии (с 0.1.0 по 0.7.0a2) уязвимы и останутся таковыми. Пакет по-прежнему скачивается примерно 28 000 раз в месяц.
Каждому, кто использует dash-uploader в продакшене, необходимо самостоятельно применить меры по смягчению уязвимости. Рекомендуемое решение — перейти на встроенный компонент dcc.Upload из Plotly Dash. Полный список вариантов см. в разделе Меры по смягчению.
| Идентификатор CVE | CVE-2026-38360 (NVD) |
| Уязвимость | Обход пути (CWE-22) |
| CVSS 3.1 | 9.8 / Критический (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| Продукт | dash-uploader |
| Затронутые версии | 0.1.0 – 0.7.0a2 (все 18 релизов) |
| Исправленная версия | отсутствует (проект архивирован 2025-07-19) |
| Вектор атаки | Удалённый, без аутентификации |
| Обнаружил | Muhammad Fitri Bin Mohd Sultan |
| Присвоен | MITRE, 2026-05-07 |
| Связанные | CVE-2026-38361 (DoS в той же библиотеке) |
Три управляемых пользователем параметра из request.form.get() в dash_uploader/httprequesthandler.py передаются напрямую в os.path.join() и os.makedirs() без какой-либо очистки или проверки:
upload_id (строки 57 → 161, BaseHttpRequestHandler.get_temp_root): управляет целевым каталогом. Атакующий может отправить upload_id=../../../../usr/local/lib/python3.10/site-packages, и файлы будут записаны в каталог пакетов Python.
resumableFilename (строки 51 → 108, BaseHttpRequestHandler._post): управляет итоговым именем файла. Атакующий может выйти за пределы каталога загрузки через имя файла даже при легитимном upload_id.
resumableIdentifier (строки 54 → 64, BaseHttpRequestHandler._post): используется с os.makedirs() для создания временного каталога. Атакующий может создавать произвольные каталоги в любом месте файловой системы.
Конечная точка загрузки (по умолчанию /API/dash-uploader) не требует аутентификации. Хук http_request_handler, добавленный в v0.5.0, позволяет выполнять проверки перед запросом через post_before(), но уязвимый метод _post() читает все параметры напрямую из request.form после возврата из хука. Хук не может очистить параметры до того, как библиотека их обработает. Разработчик, добавивший аутентификацию через хук, по-прежнему уязвим к обходу пути со стороны аутентифицированного пользователя.
# dash_uploader/httprequesthandler.py
def _post(self):
resumableFilename = request.form.get("resumableFilename", default="error", type=str)
resumableIdentifier = request.form.get("resumableIdentifier", default="error", type=str)
upload_id = request.form.get("upload_id", default="", type=str)
...
temp_root = self.get_temp_root(upload_id) # upload_id flows in here
temp_dir = os.path.join(temp_root, resumableIdentifier) # raw user input -> os.path.join
if not os.path.isdir(temp_dir):
os.makedirs(temp_dir) # raw user input -> os.makedirs
def get_temp_root(self, upload_id):
return os.path.join(self.upload_folder, upload_id) # no sanitization: ../../ escapes upload_folder
Три независимых небезопасных места (sink) имеют одну и ту же первопричину: значения формы достигают os.path.join и os.makedirs без проверки.
Неаутентифицированный удалённый атакующий отправляет HTTP POST multipart-запрос на конечную точку загрузки. Внедряя последовательности обхода пути (../) в параметр формы upload_id, атакующий получает контроль над целевым каталогом для загружаемого файла. Например, upload_id=../../../../usr/local/lib/python3.10/site-packages записывает файлы в каталог пакетов Python, что позволяет выполнить код (RCE) при следующем запуске интерпретатора за счёт автоматического выполнения .pth.
Ни аутентификация, ни сессионный токен, ни CSRF-токен не требуются. Два дополнительных параметра (resumableFilename и resumableIdentifier) предоставляют независимые векторы обхода через ту же конечную точку. Конфигурация библиотеки по умолчанию, показанная в официальной документации quickstart, эксплуатируется одной командой curl.
Произвольная запись файлов в любой каталог, доступный для записи процессу сервера. Это приводит к удалённому выполнению кода (RCE) с помощью нескольких хорошо известных примитивов:
Примитивы RCE
.pth, записанный в site-packages. Выполняет код, предоставленный атакующим, при следующем запуске интерпретатора.sitecustomize.py или usercustomize.py. Выполняется при каждом запуске Python.app.wsgi, wsgi.py). Выполняется при следующей перезагрузке воркера./etc/cron.d/, /etc/cron.hourly/, пользовательский crontab-spool) при наличии у процесса необходимых привилегий. Выполнение по расписанию./etc/systemd/system/, ~/.config/systemd/user/). Выполняется при следующем запуске службы или перезагрузке./etc/ld.so.preload, если процесс запущен от root. Предзагружает код атакующего при каждом последующем выполнении бинарных файлов.~/.bashrc, ~/.profile, ~/.bash_profile). Выполняется при следующем интерактивном входе пользователя приложения.~/.ssh/authorized_keys. Предоставляет постоянный SSH-доступ к хосту от имени пользователя приложения.Воздействие на веб-уровень
site-packages). Затрагивает каждого пользователя при каждой загрузке страницы до перезапуска приложения.Воздействие на файловую систему
os.makedirs() с неочищенным параметром resumableIdentifier (можно использовать для исчерпания inode или для поэтапной записи в несуществующие ветви каталогов).dash_uploader/httprequesthandler.pyBaseHttpRequestHandler.get_temp_root()BaseHttpRequestHandler._post()Варианты для пользователей, у которых уже развёрнут этот пакет, в порядке предпочтения:
dcc.Upload — официальный компонент загрузки, поставляемый с Plotly Dash. Файлы приходят в callback в виде base64-строки; обработчик, записывающий в файловую систему, не предоставляется, и путь назначения не контролируется клиентом, поэтому данный класс ошибок здесь неприменим. Лучше всего подходит для небольших и средних файлов. Для очень больших загрузок см. пункт 2.werkzeug.utils.secure_filename() и жёстко заданный серверный каталог назначения. Никогда не принимайте значения upload_id, имени файла или идентификатора, предоставленные клиентом, как компоненты пути.upload_id, resumableFilename и resumableIdentifier по строгому списку разрешённых значений (например, только UUID) на уровне, который переписывает или отклоняет запрос до того, как его увидит обработчик библиотеки. Хук http_request_handler библиотеки НЕ предотвращает обход пути, поскольку параметры читаются из request.form уже после возврата из хука; очистка должна выполняться выше библиотеки..., закодированные варианты (%2e%2e, ..%2f, %2e%2e%2f) или абсолютные пути.| Дата | Событие |
|---|---|
| 2026-03-17 | Уязвимость обнаружена в ходе исследования безопасности продакшен-развёртывания. |
| 2026-03-19 | Запрос на получение CVE отправлен в MITRE. |
| 2026-05-07 | MITRE присвоил CVE-2026-38360. |
| 2026-05-07 | Опубликован публичный бюллетень (advisory). |
| 2026-05-09 | Запись CVE опубликована в базе CVE MITRE и в NVD. |
0.6.1 (стабильная линия). Пререлизы доходят до 0.7.0a2.dash. Необязательная зависимость: pyyaml. Лицензия: MIT.Muhammad Fitri Bin Mohd Sultan