
Документирует CVE-2026-65320, обход пути типа tar-slip в функции untar_dir() библиотеки fastcore, с безвредным proof-of-concept, демонстрирующим произвольную запись файлов и выполнение кода.
untar_dir()Критичность: Критическая, CVSS 3.1 9.8
Вектор (v3.1): CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Затронуто: fastcore <= 2.2.30
Исправлено в: Не исправлено на момент написания
CWE: CWE-22 (Некорректное ограничение пути к каталогу, 'Обход пути')
Компонент: fastcore.xtras.untar_dir() → fastcore.xtras._unpack() → shutil.unpack_archive()
Среда выполнения с высоким риском: Python < 3.14 (в Python 3.14 безопасный фильтр tar data используется по умолчанию)
Обнаружил: Rahul Karne
CNA: VulnCheck
fastcore.xtras.untar_dir(fname, dest) — это документированный вспомогательный метод, который распаковывает архив "в dest".
Внутри он передаёт архив стандартной библиотечной функции shutil.unpack_archive() без фильтра извлечения, без проверки путей членов архива и без какой-либо проверки нахождения за пределами после извлечения.
В результате TAR-архив, члены которого содержат последовательности обхода ../ или абсолютные пути, может записывать файлы за пределами каталога, в который вызывающая сторона попросила fastcore распаковать.
Вызывающая сторона, которая обоснованно ожидает, что untar_dir(fname, dest) ограничит всё каталогом dest, вместо этого получает произвольную запись файлов в любом месте, куда процесс имеет право записи.
Произвольная запись файлов с правами процесса, вызывающего untar_dir().
В зависимости от того, что перезаписывается, это может привести к:
Доказательство концепции демонстрирует полную цепочку запись → импорт → выполнение кода от начала до конца, полностью внутри безвредного демонстрационного каталога.
fastcore — это фундаментальная зависимость во всей экосистеме fast.ai, включая:
fastainbdevghapiОн получает десятки миллионов загрузок в месяц.
untar_dir() — это примитив извлечения, лежащий в основе распространённого рабочего процесса "скачать архив, затем распаковать его", который следует той же общей схеме, что и untar_data в fastai.
Любой путь кода, который передаёт untar_dir() архив, содержимое которого не является полностью доверенным, подвержен уязвимости.
Уязвимый код находится в fastcore/xtras.py:
def _unpack(fname, out):
import shutil
shutil.unpack_archive(str(fname), str(out)) # <-- no filter, no validation
ls = out.ls()
return ls[0] if len(ls) == 1 else out
def untar_dir(fname, dest, rename=False, overwrite=False, uid=-1, gid=-1):
"untar `file` into `dest` ..."
import tempfile, shutil
dest = Path(dest)
with tempfile.TemporaryDirectory() as d:
out = Path(d) / remove_suffix(Path(fname).stem, '.tar')
out.mkdir()
...
src = _unpack(fname, out) # extraction escapes `out` here
...
shutil.move(str(src), dest)
...
return dest
untar_dir() создаёт внутренний временный каталог для извлечения:
<tempdir>/<random>/<archive-stem>/
Затем он вызывает _unpack(), который напрямую передаёт архив в:
shutil.unpack_archive()
Для TAR-архивов это в конечном итоге вызывает tarfile.extractall().
В Python < 3.14 поведение извлечения по умолчанию — fully_trusted, что означает, что члены с обходом пути могут быть обработаны.
Например, член архива, такой как:
../../../some/other/dir/file
записывается относительно каталога извлечения и, следовательно, может выйти за его пределы.
Ничто в fastcore:
filter="data" там, где это поддерживается....untar_dir() относится к shutil.unpack_archive() так, как будто это безопасный, изолированный распаковщик архивов.
Это не так.
Документация стандартной библиотеки Python предупреждает, что извлечение недоверенных архивов может создавать файлы за пределами запрошенного места назначения, и рекомендует безопасную фильтрацию извлечения для TAR-архивов.
fastcore ни передаёт подходящий фильтр извлечения, ни реализует эквивалентную защиту от выхода за пределы для поддерживаемых версий Python.
Он также не предупреждает вызывающих untar_dir() об этом чувствительном к безопасности поведении.
Члены TAR могут содержать произвольные строки путей.
В затронутых версиях Python tarfile.extractall() по умолчанию не нейтрализует автоматически последовательности обхода ...
Поэтому записи члена с обходом относительно временного каталога извлечения fastcore достаточно, чтобы выйти за пределы этого каталога и записать в любое место, куда процесс имеет право записи.
Надёжным примитивом является относительный обход, такой как:
../../../target/file
Именно эта техника используется в доказательстве концепции.
Опасный ввод — это содержимое архива, а не путь назначения, выбранный разработчиком.
Архивы часто получают из внешних или полу-доверенных источников, включая:
Поэтому любой сервис или инструмент, который скачивает архив из недоверенного или полу-доверенного источника и впоследствии обрабатывает его с помощью untar_dir(), может быть достижим.
PoC также включает вариант, который отдаёт сформированный архив с HTTP-эндпоинта, контролируемого злоумышленником, чтобы продемонстрировать этот путь доставки.
Успешная эксплуатация требует:
Приложение вызывает fastcore.xtras.untar_dir() для архива, содержимое которого находится под влиянием злоумышленника, например загруженного, скачанного или иным образом недоверенного архива.
Среда выполнения использует Python < 3.14 или другую конфигурацию, в которой безопасный фильтр извлечения TAR не применяется.
Для эскалации от произвольной записи файлов к выполнению кода процесс позже импортирует или загружает файл из места, достижимого через обход пути, например:
Произвольная запись файлов и изменение конфигурации/данных требуют только условий 1 и 2.
poc_fastcore_cna_demo.py вызывает реальный API fastcore.xtras.untar_dir().
Демонстрация намеренно безвредна.
Каждый файл, созданный PoC, остаётся внутри одного демонстрационного каталога, указанного с помощью:
--demo-root
PoC не изменяет реальные системные файлы и не обращается к облачным сервисам или учётным данным.
Выводит сведения об окружении и уязвимый исходный код для _unpack() и untar_dir().
Создаёт демонстрационную структуру, содержащую:
service_app/plugins/
Создаёт:
attacker_supplied_upload.tar
содержащий члены архива, предназначенные для указания за пределы предполагаемого корня извлечения, включая члены с обходом пути и:
service_app/plugins/startup_hook.py
def auto_extract_uploaded_archive(received_archive):
return untar_dir(
received_archive,
intended_root,
overwrite=True
)
Проверяет, что файлы были записаны за пределами предполагаемого каталога извлечения.
Имитирует перезагрузку сервиса путём импорта записанного архивом:
startup_hook.py
Безвредный хук запуска создаёт:
SERVICE_RELOAD_MARKER
Это демонстрирует полную цепочку:
Attacker-controlled archive
↓
Path traversal during extraction
↓
Arbitrary file write
↓
Write into importable / auto-loaded location
↓
Application loads attacker-controlled file
↓
Code execution
Установите затронутый пакет:
pip install fastcore
Проблема подтверждена на:
fastcore 2.2.30
Демонстрация может работать с:
fastcore, установленной через pip--sourceВсе записи PoC остаются внутри каталога, указанного через --demo-root.
Очистите предыдущий вывод PoC:
python poc_fastcore_cna_demo.py --demo-root ./poc_fastcore --clean
Запустите демонстрацию:
python poc_fastcore_cna_demo.py \
--demo-root ./poc_fastcore \
--source latest \
--no-pause
Пример сокращённого вывода:
fastcore version: 2.2.30
Outside marker exists after extraction? True
Is outside marker inside intended extraction root? False
Relative traversal marker exists after extraction? True
Is relative traversal marker inside intended extraction root? False
Service plugin file exists after extraction? True
Service reload marker exists? True
POC WORKED: fastcore extracted attacker-controlled tar members outside the intended root.
В поддерживаемых версиях Python используйте безопасный фильтр извлечения, такой как:
filter="data"
с shutil.unpack_archive() или соответствующей операцией извлечения tarfile.
Каждый член архива должен быть проверен перед извлечением.
Отклоняйте:
..Цели ссылок также должны быть разрешены и проверены перед извлечением.
Разрешайте каждый путь назначения перед записью и проверяйте, что он остаётся под предполагаемым корнем извлечения.
Концептуально:
resolved_member_path
↓
must remain inside
↓
resolved_extraction_root
Извлечение должно завершиться ошибкой, если разрешённый путь назначения выходит за пределы корня извлечения.
Регрессионные тесты должны охватывать как минимум:
../ traversal
absolute paths
Windows drive-letter paths
UNC paths
symbolic links
hard links
Чётко документируйте, предназначен ли untar_dir() для безопасной обработки недоверенных архивов.
Если небезопасное извлечение намеренно поддерживается, безопасное поведение всё равно должно быть по умолчанию.
Небезопасное поведение должно требовать явного согласия, а не молчаливого доверия содержимому архива.
До выпуска исправления на уровне пакета:
untar_dir().Основной технический дефект — это уязвимость произвольной записи файлов, вызванная обходом пути в архиве.
Однако итоговая критичность зависит от среды развёртывания и того, как untar_dir() доступен.
Репрезентативный вектор может приближаться к:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Результат:
9.8 — Critical
Если эксплуатация вместо этого требует, чтобы разработчик вручную скачал и распаковал архив, взаимодействие с пользователем и сложность атаки могут быть выше.
Итоговая оценка CVSS, следовательно, будет существенно ниже.
При оценке проблемы следует учитывать два важных фактора.
Это экземпляр давно известного класса уязвимостей небезопасного извлечения TAR, связанного с:
CVE-2007-4559
Современные выпуски Python ввели более безопасное поведение извлечения архивов.
Python 3.14 делает безопасный фильтр извлечения data поведением по умолчанию.
Следовательно, основная затронутая среда — Python < 3.14.
Однако эти среды выполнения по-прежнему широко развёрнуты.
Уязвимость становится эксплуатируемой, когда вызывающая сторона передаёт untar_dir() архив, содержимое которого контролируется злоумышленником или иным образом недоверенно.
Поэтому этот отчёт рассматривает содержимое архива как недоверенную границу безопасности.
Надёжный вспомогательный метод извлечения архивов должен безопасно завершаться с ошибкой независимо от того, как был получен архив.
Замените приведённые ниже даты-заполнители фактическими датами раскрытия.
YYYY-MM-DD — Уязвимость сообщена сопровождающим.YYYY-MM-DD — Ответ поставщика / подтверждение / отсутствие ответа.2.2.30); код извлечения остался без изменений.Обнаружено и сообщено Rahul Karne.
Документация fastcore untar_dir()
https://fastcore.fast.ai/xtras.html
Исходный код fastcore xtras.py
https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py
fastcore на PyPI
https://pypi.org/project/fastcore/
Документация Python shutil.unpack_archive()
https://docs.python.org/3/library/shutil.html#shutil.unpack_archive
Фильтры извлечения Python tarfile
https://docs.python.org/3/library/tarfile.html#extraction-filters
PEP 706 — Фильтр для tarfile.extractall
https://peps.python.org/pep-0706/
CWE-22 — Некорректное ограничение пути к каталогу
https://cwe.mitre.org/data/definitions/22.html
CVE-2007-4559 — Класс уязвимостей небезопасного извлечения TAR
https://nvd.nist.gov/vuln/detail/CVE-2007-4559
Этот репозиторий документирует результат скоординированного раскрытия информации о безопасности и предоставляет безвредное, самодостаточное доказательство концепции.
PoC записывает только внутри каталога --demo-root, указанного пользователем, и не выполняет разрушительных действий.
Любая сетевая функциональность, включённая в демонстрацию, ограничена необязательной локальной демонстрацией.
Этот материал предоставлен для защитных исследований в области безопасности и образовательных целей.
Медиа-запросы: [email protected]. Полный PoC (сервер злоумышленника, архив с обходом пути, приложение-жертва) и дополнительные технические детали доступны по запросу.