
CVE-2026-93349에 대한 Proof-of-concept으로, 악성 Data Package descriptor 경로를 통해 Frictionless <= 5.20.0rc1 explore CLI에서 발생하는 OS 명령어 주입 취약점입니다.
<= 5.20.0rc1 explore OS 명령어 주입심각도: 높음, 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 (OS 명령어 주입), CWE-20 (부적절한 입력 검증)
보고자: Sai Teja Erukude
Frictionless 버전 5.20.0rc1 프리릴리스까지 포함하여, frictionless explore CLI 명령어에 OS 명령어 주입 취약점이 존재합니다.
이 명령어는 사용자가 제공한 Data Package 디스크립터를 로드하고, 리소스 경로를 수집하며, resource.normpath를 도출하고, 해당 경로들을 하나의 명령어 문자열로 결합한 뒤, 그 문자열을 에 직접 전달합니다:
os.systempaths = [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는 resources[0].path에 무해한 마커 파일 쓰기 페이로드가 포함된 Data Package 디스크립터를 생성합니다.
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 명령어를 호출합니다. 의도된 vd 뷰어가 대화식으로 실행되지 않도록 자식 프로세스의 PATH를 지우지만, 셸이 구성된 명령어 문자열을 파싱할 때 주입된 명령어는 여전히 실행됩니다.
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까지 살아남으며, frictionless explore 실행 후 marker.txt가 생성된다는 점입니다.
페이로드는 의도적으로 무해합니다. 이는 다음 로컬 마커 문자열만 기록합니다:
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