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

Author: Carlson-JLQCreated Sep 17, 2026Updated Sep 17, 2026

问题确认 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

python
# 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)

python
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

python
# 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 b25522a0 (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!

Source: PaddlePaddle/PaddleDetection