Download vulnerable_code.py from loowr/cntk-poc: direct link, hf CLI and curl.
- Browser
- Download file 2.47 kB
-
https://huggingface.co/loowr/cntk-poc/resolve/main/vulnerable_code.py
- Command line
-
hf download hf://loowr/cntk-poc/vulnerable_code.py
-
curl -L -o vulnerable_code.py https://huggingface.co/loowr/cntk-poc/resolve/main/vulnerable_code.py
2.47 kB
| # Exact vulnerable code from CNTK v2.7.post2 | |
| # File: bindings/python/cntk/internal/__init__.py | |
| # Lines 39-66 | |
| class _UDFDeserializeCallbackWrapper(cntk_py.UDFDeserializeCallbackWrapper): | |
| def __init__(self, factory_callback_map=None): | |
| super(_UDFDeserializeCallbackWrapper, self).__init__() | |
| self.factory_callback_map = factory_callback_map | |
| def __call__(self, inputs, name, dictionary): | |
| cls = dictionary['class'] # attacker-controlled | |
| module = dictionary['module'] # attacker-controlled | |
| state = dictionary['state'] | |
| op_name = dictionary['op_name'] | |
| deserialize_method = 'deserialize' | |
| if (self.factory_callback_map and op_name in self.factory_callback_map): | |
| factory = self.factory_callback_map[op_name] | |
| else: | |
| # VULNERABILITY: exec() with attacker-controlled strings | |
| # from the model file's serialized UDF dictionary. | |
| # No validation, no sanitization, no whitelist. | |
| exec("from {} import {}".format(module, cls)) # LINE 54 | |
| # eval() with attacker-controlled cls -- secondary injection | |
| eval_str = "{0}.{1} if hasattr({0}, '{1}') else None" | |
| factory = eval(eval_str.format(cls, deserialize_method)) # LINE 56 | |
| if factory: | |
| for i in range(len(inputs)): | |
| inputs[i].__class__ = cntk.Variable | |
| return factory(list(inputs), name, state) | |
| raise ValueError("Cannot deserialize user function ...") | |
| # Serialization counterpart (lines 29-36): | |
| # Writes module/class names from a Python UDF into the model file: | |
| def _serialize(udf): | |
| dictionary = {} | |
| dictionary['class'] = udf.__class__.__name__ # attacker can craft this | |
| dictionary['module'] = udf.__class__.__module__ # attacker can craft this | |
| dictionary['op_name'] = udf.op_name | |
| dictionary['state'] = udf.serialize() | |
| dictionary['version'] = _serialization_version | |
| return dictionary | |
| # Attack chain: | |
| # | |
| # 1. Attacker crafts a CNTK .model file (protobuf Dictionary) | |
| # with 'module' = "os" and 'class' containing injected code | |
| # | |
| # 2. Victim calls cntk.Function.load("malicious.model") | |
| # | |
| # 3. CNTK protobuf deserializer extracts the Dictionary | |
| # | |
| # 4. _UDFDeserializeCallbackWrapper.__call__() is invoked | |
| # | |
| # 5. exec("from {} import {}".format(module, cls)) | |
| # executes the attacker's code | |
| # | |
| # 6. Arbitrary Python code execution on victim's machine | |