
권고: CVE-2026-38361 dash-uploader (Python/PyPI)의 여러 DoS 취약점 (CWE-400/CWE-670)
fohrloop/dash-uploader (Python, PyPI)에서 발견된 다중 인증되지 않은 서비스 거부(DoS) 문제로, (이에 국한되지 않음) 메모리 부족(OOM) 프로세스 충돌, 파일을 0바이트로 자르기, 영구적인 디스크 고갈, 문서화된 max_file_size 제한의 완전한 우회 등을 포함합니다. 동일한 무결한 매개변수 집합을 통해 추가 리소스 남용 경로가 존재합니다.
저장소는 2025-07-19에 아카이브되었으며 활성 관리자가 없습니다. 출시된 모든 버전(0.1.0부터 0.7.0a2까지)이 영향을 받으며 앞으로도 그럴 것입니다. 이 패키지는 여전히 월간 약 28,000회 다운로드됩니다.
프로덕션에서 dash-uploader를 실행하는 사람은 스스로 완화 조치를 적용해야 합니다. 권장되는 수정 방법은 Plotly Dash의 내장 dcc.Upload 구성 요소로 마이그레이션하는 것입니다. 전체 옵션은 완화 조치를 참조하세요.
| CVE ID | CVE-2026-38361 (NVD) |
| 취약점 | 통제되지 않은 리소스 소비 (CWE-400), 항상 잘못된 제어 흐름 구현 (CWE-670) |
| CVSS 3.1 | 7.5 / 높음 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) |
| 제품 | dash-uploader |
| 영향받는 버전 | 0.1.0부터 0.7.0a2까지 (전체 18개 릴리스) |
| 수정 버전 | 없음 (프로젝트가 2025-07-19에 아카이브됨) |
| 공격 벡터 | 원격, 인증되지 않음 |
| 발견자 | Muhammad Fitri Bin Mohd Sultan |
| 할당 기관 | MITRE, 2026-05-07 |
| 관련 | CVE-2026-38360 (동일 라이브러리의 경로 순회) |
dash-uploader HTTP 핸들러는 바운드 검사, 속도 제한, 정리 메커니즘 없이 메모리 할당, 파일 작업 및 디렉토리 생성으로 이어지는 공격자 제어 매개변수가 포함된 인증되지 않은 POST 요청을 허용합니다. 동일한 코드 경로에 4가지 독립적인 문제가 있습니다.
7.7GB 시스템에서 확인됨: resumableTotalChunks=30000000이 포함된 5개의 동시 POST 요청이 2초 이내에 Linux OOM 킬러를 트리거했습니다. 커널 로그에서 확인됨:
Out of memory: Killed process 24203 (python3) total-vm:8302276kB, anon-rss:7068012kB
각 요청은 range(1, resumableTotalChunks + 1)에 대한 리스트 컴프리헨션을 통해 약 2.9GB를 할당합니다. 서버 프로세스가 종료되고 수동 재시작이 있을 때까지 애플리케이션을 완전히 사용할 수 없게 됩니다.
42바이트의 데이터를 포함한 파일이 resumableTotalChunks=0인 단일 POST 요청을 통해 0바이트로 축소되었습니다. 근본 원인은 Python의 all()이 빈 반복 가능 객체에 대해 True를 반환하여 업로드 핸들러가 0개의 청크를 완료된 업로드로 처리하도록 속이는 것입니다. 기존 파일은 os.unlink()를 통해 삭제되고 빈 파일로 대체됩니다.
청크 파일이 있는 10개의 고아 임시 디렉토리가 디스크에 영구적으로 생성되어 지속되었습니다. 모든 소스 파일에서 cleanup, ttl, expire, garbage, purge, cron, schedule, periodic에 대한 코드베이스 전체 검색에서 0개의 결과가 반환되었습니다. 유일한 정리 호출(shutil.rmtree)은 완료된 업로드에만 독점적으로 실행됩니다. 불완전한 세션에서 디스크 공간을 회수하는 메커니즘이 없습니다.
max_file_size 우회 (확인됨)서버가 HTTP 200으로 resumableTotalSize=999999999999(~999GB)를 주장하는 파일에 대해 5MB 청크를 수락했습니다. max_file_size 매개변수는 React JavaScript 구성 요소에만 전달됩니다. 서버는 파일 크기, 청크 크기, Content-Length 또는 Flask MAX_CONTENT_LENGTH를 절대 확인하지 않습니다. max_file_size=10을 설정한 개발자는 서버 측 보호 기능이 없습니다.
# dash_uploader/httprequesthandler.py
def _post(self):
resumableTotalChunks = request.form.get("resumableTotalChunks", type=int) # 공격자 제어, 바운드 없음
...
chunk_paths = [
os.path.join(temp_dir, get_chunk_name(resumableFilename, x))
for x in range(1, resumableTotalChunks + 1) # 바운드 없음; 예: 30M -> ~2.9 GB -> OOM
]
upload_complete = all([os.path.exists(p) for p in chunk_paths]) # all([])은 True -> 청크가 0이면 잘림
if upload_complete:
target_file_name = os.path.join(temp_root, resumableFilename)
if os.path.exists(target_file_name):
os.unlink(target_file_name) # 기존 파일 삭제
with open(target_file_name, "ab") as target_file:
for p in chunk_paths: # 빈 리스트 -> 빈 파일 기록
...
동일한 코드 경로가 OOM(큰 resumableTotalChunks)과 파일 잘림 프리미티브(resumableTotalChunks=0)를 모두 생성합니다.
공격자는 /API/resumable 엔드포인트에 인증되지 않은 POST 요청을 보냅니다.
resumableTotalChunks=30000000이 포함된 5개의 동시 요청이 각각 약 2.9GB를 할당하여 OOM 킬러를 트리거합니다.resumableTotalChunks=0을 보냅니다. Python all([])=True가 서버를 속여 대상 파일을 빈 내용으로 덮어씁니다.인증이나 권한이 필요하지 않습니다.
resumableTotalChunks)의 무제한 메모리 할당으로 인한 Linux OOM 킬러를 통한 서버 프로세스 충돌resumableIdentifier로 os.makedirs()를 통한 임의 깊이 디렉토리 생성으로 인한 파일 시스템 inode 고갈resumableTotalChunks=0일 때 빈 반복 가능 객체에 대해 Python all()이 True를 반환하여 발생하는 파일 0바이트 잘림을 통한 데이터 파괴max_file_size가 클라이언트 측 JavaScript에서만 적용되고 서버 측 핸들러가 크기 검증을 전혀 수행하지 않으며 Flask MAX_CONTENT_LENGTH를 설정하지 않기 때문에 모든 파일 크기 제한 우회dash_uploader/httprequesthandler.py (BaseHttpRequestHandler._post 메서드)dash_uploader/upload.py (Upload 함수, max_file_size 매개변수)dash_uploader/configure_upload.py (누락된 MAX_CONTENT_LENGTH)현재 배포된 사용자를 위한 선호도 순 옵션:
dcc.Upload로 마이그레이션: Plotly Dash와 함께 제공되는 공식 업로드 구성 요소입니다. 청크 수 매개변수, 디스크상 임시 상태가 없으며 Flask MAX_CONTENT_LENGTH를 준수합니다. 여기에 있는 4가지 문제 중 어느 것도 적용되지 않습니다. 중소형 파일에 가장 적합합니다. 매우 큰 업로드의 경우 항목 2를 참조하세요.MAX_CONTENT_LENGTH), 클라이언트가 제공하는 청크 수에 대한 바운드, 허용된 파일 이름에 대한 허용 목록을 포함합니다.MAX_CONTENT_LENGTH를 설정하고(라이브러리가 설정하지 않음), 다음 조건 중 하나라도 해당되는 경우 애플리케이션 또는 리버스 프록시 계층에서 입력을 거부합니다.
resumableTotalChunks <= 0resumableTotalChunks가 합리적인 바운드를 초과하는 경우(예: 10,000)resumableTotalSize가 개발자가 구성한 max_file_size를 초과하는 경우| 날짜 | 이벤트 |
|---|---|
| 2026-03-19 | 프로덕션 배포에 대한 보안 연구 중 취약점 발견. |
| 2026-03-22 | MITRE에 CVE 요청 제출. |
| 2026-05-07 | MITRE에서 CVE-2026-38361 할당. |
| 2026-05-07 | 공개 권고 게시. |
| 2026-05-09 | MITRE CVE 데이터베이스 및 NVD에 CVE 레코드 게시. |
0.6.1 (안정 라인). 사전 릴리스는 0.7.0a2까지 확장됩니다.dash. 선택적 종속성: pyyaml. 라이선스: MIT.Muhammad Fitri Bin Mohd Sultan