NeMo
security
poc
asmit007's picture
Upload 4 files
b5e1847 verified
|
Raw
History Blame Contribute Delete
2.4 kB
metadata
license: apache-2.0
tags:
  - security
  - poc
  - nemo

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.