python.lang.security.deserialization.avoid-pyyaml-load.avoid-pyyaml-load

Community Favorite
profile photo of returntocorpreturntocorp
Author
47,520
Download Count*

Detected a possible YAML deserialization vulnerability. yaml.unsafe_load, yaml.Loader, yaml.CLoader, and yaml.UnsafeLoader are all known to be unsafe methods of deserializing YAML. An attacker with control over the YAML input could create special YAML input that allows the attacker to run arbitrary Python code. This would allow the attacker to steal files, download and install malware, or otherwise take over the machine. Use yaml.safe_load or yaml.SafeLoader instead.

Run Locally

Run in CI

Defintion

rules:
  - id: avoid-pyyaml-load
    metadata:
      owasp:
        - A08:2017 - Insecure Deserialization
        - A08:2021 - Software and Data Integrity Failures
      cwe:
        - "CWE-502: Deserialization of Untrusted Data"
      references:
        - https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation
        - https://nvd.nist.gov/vuln/detail/CVE-2017-18342
      category: security
      technology:
        - pyyaml
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - audit
      likelihood: LOW
      impact: MEDIUM
      confidence: MEDIUM
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
    languages:
      - python
    message: Detected a possible YAML deserialization vulnerability.
      `yaml.unsafe_load`, `yaml.Loader`, `yaml.CLoader`, and `yaml.UnsafeLoader`
      are all known to be unsafe methods of deserializing YAML. An attacker with
      control over the YAML input could create special YAML input that allows
      the attacker to run arbitrary Python code. This would allow the attacker
      to steal files, download and install malware, or otherwise take over the
      machine. Use `yaml.safe_load` or `yaml.SafeLoader` instead.
    fix-regex:
      regex: unsafe_load
      replacement: safe_load
      count: 1
    severity: ERROR
    patterns:
      - pattern-inside: |
          import yaml
          ...
      - pattern-not-inside: |
          $YAML = ruamel.yaml.YAML(...)
          ...
      - pattern-either:
          - pattern: yaml.unsafe_load(...)
          - pattern: yaml.load(..., Loader=yaml.Loader, ...)
          - pattern: yaml.load(..., Loader=yaml.UnsafeLoader, ...)
          - pattern: yaml.load(..., Loader=yaml.CLoader, ...)
          - pattern: yaml.load_all(..., Loader=yaml.Loader, ...)
          - pattern: yaml.load_all(..., Loader=yaml.UnsafeLoader, ...)
          - pattern: yaml.load_all(..., Loader=yaml.CLoader, ...)

Examples

avoid-pyyaml-load.py

import yaml


#ruleid:avoid-pyyaml-load
yaml.unsafe_load("!!python/object/new:os.system [echo EXPLOIT!]")

def thing(**kwargs):
    #ruleid:avoid-pyyaml-load
    yaml.unsafe_load("!!python/object/new:os.system [echo EXPLOIT!]", **kwargs)

def other_thing(**kwargs):
    #ruleid:avoid-pyyaml-load
    yaml.load("!!python/object/new:os.system [echo EXPLOIT!]", Loader=yaml.Loader, **kwargs)

def other_thing_two(**kwargs):
    #ruleid:avoid-pyyaml-load
    yaml.load("!!python/object/new:os.system [echo EXPLOIT!]", Loader=yaml.UnsafeLoader, **kwargs)

def other_thing_three(**kwargs):
    #ruleid:avoid-pyyaml-load
    yaml.load("!!python/object/new:os.system [echo EXPLOIT!]", Loader=yaml.CLoader, **kwargs)

def other_thing_four(**kwargs):
    #ruleid:avoid-pyyaml-load
    yaml.load_all("!!python/object/new:os.system [echo EXPLOIT!]", Loader=yaml.Loader, **kwargs)

def other_thing_five(**kwargs):
    #ruleid:avoid-pyyaml-load
    yaml.load_all("!!python/object/new:os.system [echo EXPLOIT!]", Loader=yaml.UnsafeLoader, **kwargs)

def other_thing_six(**kwargs):
    #ruleid:avoid-pyyaml-load
    yaml.load_all("!!python/object/new:os.system [echo EXPLOIT!]", Loader=yaml.CLoader, **kwargs)

def this_is_ok(stream):
    #ok:avoid-pyyaml-load
    return yaml.load(stream, Loader=yaml.CSafeLoader)

def this_is_also_ok(stream):
    #ok:avoid-pyyaml-load
    return yaml.load(stream, Loader=yaml.SafeLoader)

def this_is_additionally_ok(stream):
    #ok:avoid-pyyaml-load
    return yaml.load_all(stream, Loader=yaml.CSafeLoader)

def this_is_ok_too(stream):
    #ok:avoid-pyyaml-load
    return yaml.load_all(stream, Loader=yaml.SafeLoader)

def this_is_ok_as_well(stream):
    #ok:avoid-pyyaml-load
    return yaml.load(stream, Loader=yaml.BaseLoader)

def this_is_ok_too_two(stream):
    #ok:avoid-pyyaml-load
    return yaml.load_all(stream, Loader=yaml.BaseLoader)

def check_ruamel_yaml():
    from ruamel.yaml import YAML
    yaml = YAML(typ="rt")
    # ok:avoid-pyyaml-load
    yaml.load("thing.yaml")
    # ok:avoid-pyyaml-load
    yaml.load_all("thing.yaml")