training data pickle.load (no integrity check) + Agent Repository importlib.import_module on remote JSON (no allowlist)
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 58.8k
- Forks
- 8.5k
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 109
Description
What
Two unsafe primitives in crewAI's training and agent-repository paths:
-
Unsafe deserialization on training data —
PickleHandler.load(lib/crewai/src/crewai/utilities/file_handler.py:166) doesreturn pickle.load(file) # noqa: S301with no integrity check. Thetraining_data.pkl/ trained-agents pkl file lives in the working directory and is loaded on every task execution of a training-enabled crew (agent/core.py:1282 _training_handler→:1309 _use_trained_data). Any actor that can write the cwd (shared CI, multi-user host, co-tenant process) plants a malicious pickle → arbitrary code execution on the next trained crew kickoff. The loaded values are also injected verbatim into agent prompts as instructions (core.py:1289-1292). -
Unsandboxed module import from remote Agent Repository —
load_agent_from_repository(lib/crewai/src/crewai/utilities/agent_utils.py:1240-1243) takes tool definitions from the AMPget_agent()response and doesimportlib.import_module(tool["module"])+tool_class(**tool["init_params"])with no module allowlist. A compromised AMP endpoint, MITM, or a malicious agent published to the org yields import of any importable module plus constructor execution with attacker-controlled arguments — RCE without any local file write.
Checked at b10c4ff (HEAD main).
How to reproduce
# 1. pickle.load runs arbitrary code (the pattern PickleHandler.load uses)
import pickle
class P:
def __reduce__(self):
return (print, ("RCE via training_data.pkl",))
# write pickle.dumps(P()) to training_data.pkl in the crew's cwd → loads on next trained task
# 2. remote import — any module named in the AMP get_agent() response is imported
# agent_utils.py:1240: module = importlib.import_module(tool["module"]) # no allowlist
# agent_utils.py:1243: tool_class(**tool["init_params"]) # attacker-controlled args
Impact
- Training pickle: RCE as the crew process. Requires write access to the cwd (training is opt-in via
crew.train(), but oncetraining_data.pklexists the load is automatic on every task). - Remote import: RCE via compromised/MIITM AMP endpoint or malicious org-published agent. Requires
Agent(from_repository=...)usage.
Suggested change
- Replace
pickle.loadon training data with a safe format (JSON) or add an integrity check (HMAC/signature over the pkl file). The# noqa: S301suppression acknowledges the risk but doesn't mitigate it. - Validate
tool["module"]against an allowlist beforeimportlib.import_module; reject unknown modules or require explicit operator approval for each import.
Happy to open a PR for either.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with PickleHandler.load in lib/crewai/src/crewai/utilities/file_handler.py and the _training_handler/_use_trained_data paths in agent/core.py, then inspect load_agent_from_repository in utilities/agent_utils.py. Trace how training files and AMP tool definitions reach these entry points, and review existing security tests if present. Done means both paths reject untrusted execution while preserving supported training and repository behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend-api-design, security
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100