Safeguard
Vulnerability Analysis

CVE-2017-18342: Arbitrary code execution via PyYAML yaml....

CVE-2017-18342 lets attackers achieve remote code execution via PyYAML's yaml.load(), which deserialized untrusted YAML into live Python objects by default.

Aman Khan
AppSec Engineer
8 min read

PyYAML, the de facto YAML parsing library for the Python ecosystem, shipped with a default deserialization behavior that allowed arbitrary Python code execution when an application fed it untrusted input. Tracked as CVE-2017-18342, the flaw lives in the yaml.load() API: prior to version 5.1, calling yaml.load() without explicitly specifying a restrictive loader used a full-featured loader capable of reconstructing arbitrary Python objects from YAML tags such as !!python/object/new and !!python/object/apply. An attacker who could supply YAML content to an application using this pattern could embed a tag that instantiates a dangerous callable — for example os.system or subprocess.Popen — and achieve remote code execution the moment the document was parsed.

This is not an exotic parser bug requiring memory corruption or a race condition. It's a design-level trust problem: yaml.load() behaved less like a data parser and more like pickle.loads(), deserializing into live Python objects rather than plain data structures. Any service, CLI tool, CI pipeline, or config loader that passed externally-supplied YAML into yaml.load() without pinning a safe loader was, by definition, exposing a code execution primitive to whoever controlled that YAML.

Affected Versions and Components

CVE-2017-18342 affects PyYAML versions prior to 5.1. The vulnerable surface is the yaml.load() function when invoked without an explicit, restricted Loader argument — the library's historical default behavior. PyYAML is a foundational dependency across the Python ecosystem, so the practical blast radius extends well beyond direct callers of the library. Any tool, framework, or internal service that:

  • Parses YAML configuration files supplied by users or external systems
  • Deserializes YAML-formatted data received over a network API
  • Loads YAML from CI/CD pipeline artifacts, webhook payloads, or plugin manifests

...using an unpinned or pre-5.1 PyYAML dependency and a bare yaml.load(data) call was potentially exposed. Because PyYAML is a transitive dependency pulled in by an enormous number of packages (configuration management tools, data pipelines, deployment tooling, and general-purpose Python applications), organizations frequently carried this exposure without a direct, first-party call to yaml.load() anywhere in their own code — it arrived through a dependency several layers removed from application code.

The fix, shipped in PyYAML 5.1, introduced yaml.safe_load() as a hardened alternative, added explicit FullLoader and SafeLoader classes, and began emitting deprecation warnings when yaml.load() was called without an explicit Loader argument — nudging developers toward declaring their trust boundary rather than inheriting an unsafe default silently. Later PyYAML releases continued tightening this behavior, progressively making the unsafe default harder to invoke by accident.

CVSS, EPSS, and KEV Context

NVD lists a CVSS v3.1 base score of 9.8 (Critical) for CVE-2017-18342, reflecting a network-exploitable vector with low attack complexity, no privileges required, no user interaction, and full impact to confidentiality, integrity, and availability — consistent with an unauthenticated, arbitrary code execution primitive once attacker-controlled YAML reaches the vulnerable call.

That severity score describes the ceiling of impact, not the ease of reaching it in every deployment. Exploitability in practice depends entirely on whether a given application actually routes untrusted, attacker-influenced data into yaml.load(). In services designed for that pattern (config ingestion endpoints, plugin/manifest loaders, multi-tenant YAML processing), the CVSS score's real-world implications match its rating closely. In applications where YAML is only ever loaded from trusted, developer-authored files, the exposure is comparatively lower — though still worth remediating, since trust assumptions about "internal" config files erode over time as pipelines and integrations evolve.

We're not aware of CVE-2017-18342 appearing on CISA's Known Exploited Vulnerabilities (KEV) catalog, and we don't have a precise, current EPSS score to cite with confidence here. That absence from KEV shouldn't be read as "low risk" — it more likely reflects that this is a widespread library-level design flaw rather than a single named product with a canonical exploited instance to track. The exploitation pattern (unsafe deserialization) is well understood and trivially weaponizable once a vulnerable call site is identified; the limiting factor for real-world attacks is discovering where a specific target application exposes attacker-controlled YAML to an unsafe loader, not any technical difficulty in crafting the payload itself.

Timeline

The unsafe-by-default behavior in yaml.load() reflects a design choice present in PyYAML for years before it was formally tracked as a CVE — it belongs to the same well-known class of YAML deserialization issues that has affected YAML libraries across multiple languages (Ruby's Psych, Java's SnakeYAML, and others have had comparable CVEs for the same underlying pattern: full object deserialization by default). PyYAML's own issue tracker and documentation had flagged the risk of yaml.load() on untrusted input for a long time before it received a dedicated identifier.

CVE-2017-18342 was assigned in the 2017 CVE numbering block but did not become broadly visible in vulnerability databases until it was published later, a common lag pattern for issues that are well known within a project community before formal CVE tracking catches up. The remediation itself arrived with PyYAML 5.1, which added safe_load()/SafeLoader, full_load()/FullLoader, and warnings discouraging the bare, unsafe call pattern. Subsequent PyYAML releases continued to reduce how easy it was to fall into the unsafe default, reinforcing safe-by-default deserialization as the expected pattern for the library going forward.

Remediation Steps

Fixing this issue is straightforward once a vulnerable call site is identified, but finding every call site across a codebase and its dependency tree is the harder part in practice.

  1. Upgrade PyYAML. Move to PyYAML 5.1 or later across all environments — application code, internal tooling, and any bundled or vendored copies. Check requirements.txt, pyproject.toml, Pipfile, and lockfiles for pinned pre-5.1 versions, including transitive pins introduced by other dependencies.

  2. Replace unsafe yaml.load() calls. Audit every call to yaml.load() in first-party code and internal tooling. Where the input is not fully trusted (or where trust can't be firmly guaranteed for the lifetime of the code), replace it with:

    • yaml.safe_load(data) for the common case of loading plain data structures, or
    • yaml.load(data, Loader=yaml.SafeLoader) when you need explicit control alongside other loader options.
  3. Never use FullLoader for untrusted input. FullLoader closes off the ability to invoke arbitrary Python callables directly but still supports Python-specific tags and is not a substitute for SafeLoader when the source of the YAML isn't fully trusted.

  4. Audit transitive dependencies. Since PyYAML is often pulled in indirectly, use pip show pyyaml, dependency graph tooling, or an SBOM to confirm which packages in your environment bundle or require PyYAML, and at what version. A safe-looking application can still be exposed through a plugin, adapter, or internal library that calls yaml.load() unsafely on your behalf.

  5. Add a static or CI check. A simple grep or AST-based lint rule flagging bare yaml.load() calls without an explicit safe Loader argument is a low-cost way to prevent regressions after the initial fix.

  6. Treat this as a pattern, not a one-off. The same root cause — deserializing into live objects instead of plain data — recurs in pickle, unsafe XML parsing, and other Python serialization formats. Teams that fix PyYAML in isolation but leave the broader unsafe-deserialization pattern unaddressed elsewhere in the codebase tend to reintroduce equivalent issues later.

How Safeguard Helps

CVE-2017-18342 is a textbook example of why dependency-level vulnerability visibility has to extend past "is the package version patched" and into "is the vulnerable API actually reachable in this codebase." A blanket PyYAML version bump satisfies a scanner's version check, but it doesn't tell you whether some internal tool three services away still calls bare yaml.load() on a webhook payload.

Safeguard's software supply chain security platform is built for exactly this gap. It continuously inventories the open-source components across your codebases and build pipelines — including transitive dependencies like PyYAML pulled in by other libraries — and maps known CVEs like this one against the actual versions in use, so pre-5.1 PyYAML instances surface immediately rather than being buried in a dependency tree. Beyond version matching, Safeguard helps teams prioritize remediation using real exploitability and reachability signals instead of CVSS alone, so a critical-severity deserialization flaw in a component that's genuinely exposed to untrusted input gets triaged ahead of the same CVE sitting in a code path that never touches external data.

For vulnerability classes like unsafe YAML deserialization, where the fix requires both a dependency upgrade and a code-level change, Safeguard's continuous monitoring flags newly introduced risky patterns going forward — catching a reintroduced bare yaml.load() call in a future pull request before it ships, rather than relying on a one-time audit that goes stale. Combined with SBOM generation and policy enforcement in CI/CD, this gives security and engineering teams a durable answer to "are we still exposed to CVE-2017-18342, anywhere in our stack" rather than a one-time snapshot answer that ages out the moment a new service or dependency is added.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.