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