
Документирует 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
содержащий члены архива, предназначенные для указания за пределы предполагаемого корня извлечения, включая члены с обходом пути и: