
Proof-of-concept для CVE-2026-93349 — внедрение команд ОС в Frictionless <= 5.20.0rc1 через CLI explore посредством путей в дескрипторе вредоносного Data Package.
<= 5.20.0rc1 explore FrictionlessУровень опасности: Высокий, CVSS 3.1 8.8
Вектор: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
Затронуто: все версии Frictionless <= 5.20.0rc1
Компонент: frictionless.console.commands.explore
CWE: CWE-78 (Внедрение команд ОС), CWE-20 (Некорректная проверка входных данных)
Обнаружено и сообщено: Sai Teja Erukude
Версии Frictionless вплоть до предрелиза 5.20.0rc1 включительно содержат
уязвимость внедрения команд ОС в CLI-команде frictionless explore.
Команда загружает предоставленный пользователем дескриптор Data Package, собирает
пути к ресурсам, вычисляет resource.normpath, объединяет эти пути в одну
командную строку и передаёт её напрямую в os.system:
paths = [resource.normpath for resource in resources if resource.normpath]
os.system(f"vd {' '.join(paths)}")
Поскольку resources[*].path — это управляемые дескриптором метаданные из
datapackage.json, злоумышленник может включить метасимволы оболочки в путь
ресурса. Когда жертва запускает frictionless explore на вредоносном
дескрипторе, оболочка интерпретирует эти метасимволы и выполняет внедрённую
команду.
Злоумышленник может выполнить произвольные команды в контексте безопасности
пользователя, запускающего frictionless explore.
Это может затронуть рабочие процессы, в которых пользователи просматривают, проверяют или исследуют дескрипторы Data Package из недоверенных проектов, архивов, репозиториев или общих наборов данных.
В зависимости от локального окружения это может позволить:
Уязвимый путь — это команда explore:
frictionless explore <package>
-> Package.from_descriptor(...)
-> Resource.list()
-> resource.normpath
-> os.system(f"vd {' '.join(paths)}")
PoC генерирует дескриптор Data Package, в котором resources[0].path содержит
безвредную полезную нагрузку, записывающую файл-маркер.
В Windows сгенерированный путь использует разделители команд &:
data.csv & echo frictionless_explore_command_injection_triggered>"...\marker.txt" & rem
В POSIX-системах сгенерированный путь использует разделители команд в виде точки с запятой:
data.csv; printf %s frictionless_explore_command_injection_triggered > .../marker.txt; #
Запускающий скрипт вызывает реальную команду frictionless explore из
установленного пакета. Он очищает PATH для дочернего процесса, чтобы
предполагаемый просмотрщик vd не запускался интерактивно, но внедрённая команда
всё равно выполняется, когда оболочка разбирает сформированную командную строку.
package_builder.py создаёт контролируемый
злоумышленником дескриптор Data Package и безвредный CSV-файл.
run_poc.py проверяет дескриптор, вызывает
frictionless explore и выводит доказательства выполнения.
Запускать только в локальной тестовой среде:
python -m venv .venv
.venv\Scripts\activate
python -m pip install -r requirements.txt
python -B run_poc.py
Включённый requirements.txt фиксирует frictionless==5.20.0rc1 — последний
подтверждённо уязвимый предрелиз в этом диапазоне.
Ожидаемые доказательства на уязвимых версиях:
frictionless_version : 5.20.0rc1
installed_package : True
descriptor_valid : True
listed_resource_count : 1
raw_path_contains_payload : True
normpath_contains_payload : True
explore_returncode : 0
viewer_launch_disabled : True
marker_exists : True
marker_contents
frictionless_explore_command_injection_triggered
success : True
Важным доказательством является то, что вредоносный дескриптор действителен,
управляемый дескриптором путь сохраняется в resource.normpath, и marker.txt
создаётся после запуска frictionless explore.
Полезная нагрузка намеренно безвредна. Она лишь записывает эту локальную строку- маркер:
frictionless_explore_command_injection_triggered
Она не порождает сессию оболочки, не подключается к сетевому сервису, не читает секреты, не удаляет данные и не изменяет файлы за пределами каталога PoC.
Избегайте передачи управляемых дескриптором путей в оболочку.
Замените вызов через оболочку на subprocess.run с использованием вектора
аргументов:
subprocess.run(["vd", *paths], check=False)
Это передаёт каждый путь ресурса как литеральный аргумент в vd. Метасимволы
оболочки внутри пути остаются обычными символами внутри этой единственной записи
argv и не выполняются.
Чёрный список конкретных символов пути слабее, поскольку синтаксис оболочки различается в зависимости от платформы и оболочки. Корневой причиной является граница вызова команды, поэтому надёжное исправление должно избегать интерпретации оболочкой.
См. fix.md для предлагаемого патча.
Обнаружено и сообщено Sai Teja Erukude.
Раскрытие координировалось через VulnCheck.
exploit_walkthrough.mdfix.md