aslein1413's picture
Upload folder using huggingface_hub
9b18c5a verified
|
Raw
History Blame Contribute Delete
1.76 kB
# PoC β€” code execution when loading a CNTK v2 (.cntk) model
Loading an untrusted CNTK v2 model with the normal `cntk.Function.load()` runs code
embedded in the file. A CNTK graph can hold a Python `UserFunction` node, and the node
stores the function's `module` and `class` as plain strings. On load CNTK's UDF
deserializer does `exec("from {module} import {class}")` on those attacker-controlled
strings, so the file alone decides what gets imported and run.
`model_inject_linux.cntk` needs no external file: the `module` is byte-patched to
`builtins` and the `class` string carries a newline-injected payload, so loading it
runs `os.system('id ...')` and drops `/tmp/PWNED_cntk`.
## Files
- `model_inject_linux.cntk` β€” self-contained malicious model (stdlib-only, no attacker .py).
- `verify.py` β€” loads it and prints the marker.
- `model_evil.cntk` + `evil_udf.py` β€” the planted-module variant (module named in the model is imported from sys.path).
- `build_inject_linux.py` β€” how the injected model was built.
- `Dockerfile` β€” the exact clean-room image (python:3.6 + cntk 2.7 + OpenMPI 1.10).
## Reproduce
CNTK 2.7 is py3.5/3.6 only. In the clean room I confirmed it on a stock wheel:
```
docker build -t cntk-poc . # python:3.6-slim + pip install cntk==2.7 + OpenMPI 1.10
docker run --rm -v "$PWD":/poc -w /poc cntk-poc python verify.py
# marker after : True
# uid=0(root) gid=0(root) groups=0(root)
# INJECTED_NO_EXTERNAL_FILE
```
Or on any py3.6 box: `pip install cntk==2.7` then `python verify.py`.
## Why it matters
Any service that loads a user-supplied `.cntk` runs attacker code as the loading process.
It is the default load path, no flag turns it off, and ModelScan does not scan `.cntk`
at all, so the file looks clean.