Skip to content

[Bug] config loading uses yaml.load(..., Loader=yaml.Loader); a crafted yaml executes arbitrary code #9502

Description

@Carlson-JLQ

问题确认 Search before asking

  • 我已经查询历史issue,没有发现相似的bug。I have searched the issues and found no similar bug report.

Bug组件 Bug Component

Deploy, Training, Validation

Bug描述 Describe the Bug

Summary

PaddleDetection uses YAML for configuration, but config loading uses PyYAML's
Loader=yaml.Loader (the full loader, which can construct arbitrary Python objects) —
equivalent to yaml.unsafe_load. Loading a crafted config file therefore executes arbitrary
code inside that process
.

Locations

# ppdet/core/workspace.py:86-88
def _load_config_with_base(file_path):
    with open(file_path) as f:
        file_cfg = yaml.load(f, Loader=yaml.Loader)     # <- not safe_load

Same pattern (-o k=v command-line overrides; input comes from the command line, see Impact):

  • ppdet/utils/cli.py:69, :77
  • deploy/pipeline/cfg_utils.py:30, :38

Root cause

yaml.Loader is not a safe loader: it supports tags such as !!python/object/apply, which let
yaml.load invoke arbitrary Python callables during parsing. The PyYAML docs explicitly recommend
yaml.safe_load (or SafeLoader) for untrusted input.

Steps to reproduce (pure stdlib — no PaddlePaddle/CUDA needed)

import os, tempfile, yaml

p = tempfile.mktemp(suffix=".yml")
with open(p, "w") as f:
    f.write('!!python/object/apply:os.system ["touch /tmp/PD_YAML_RCE"]\n')

# ----- equivalent to ppdet/core/workspace.py:88 -----
with open(p) as f:
    yaml.load(f, Loader=yaml.Loader)
# ---------------------------------------------------

print("marker exists:", os.path.exists("/tmp/PD_YAML_RCE"))

Expected results

Parsing a config should only build plain data structures; !!python/... tags should be rejected
with an error rather than executing any Python call (i.e. the SafeLoader / safe_load behavior).

Actual results

marker exists: True        <- /tmp/PD_YAML_RCE was created; the command executed

Impact and preconditions

PaddleDetection's YAML configs are assets that get shared, downloaded and copied around in ML
workflows:

  • ppdet/utils/download.py::get_config_path(url) downloads a configs tar and consumes it;
  • in practice users routinely use configs provided by others (model zoo, tutorials, configs pushed
    by an internal platform).

So if an attacker can get a user to load a crafted config (or tamper with the configs download
source), they get arbitrary code execution at that process's privilege level.

To be precise: a local, self-authored config does not constitute a trust boundary (users run
their own configs), so exploitability depends on whether the config comes from an untrusted source —
which is frequently the case in PaddleDetection's actual usage, especially via the
get_config_path download path above.

Suggested fix

# ppdet/core/workspace.py:88
file_cfg = yaml.load(f, Loader=yaml.SafeLoader)   # or yaml.safe_load(f)

Same for ppdet/utils/cli.py:69/77 and deploy/pipeline/cfg_utils.py:30/38.
If specific object construction is genuinely needed, register a controlled constructor explicitly
rather than enabling the full Loader.

复现环境 Environment

  • OS: Ubuntu 22.04
  • PaddlePaddle: N/A - reproducible at the plain Python + PyYAML level; PaddlePaddle is not needed
  • PaddleDetection: local clone, commit b25522a (2026-03-16); also present on develop
  • Python: 3.10.12
  • PyYAML: any version (reproduces on all)
  • CUDA: N/A
  • CUDNN: N/A
  • GCC: N/A

Bug描述确认 Bug description confirmation

  • 我确认已经提供了Bug复现步骤、代码改动说明、以及环境信息,确认问题是可以复现的。I confirm that the bug replication steps, code change instructions, and environment information have been provided, and the problem can be reproduced.

是否愿意提交PR? Are you willing to submit a PR?

  • 我愿意提交PR!I'd like to help by submitting a PR!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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