Safeguard
Vulnerability Analysis

IPython crafted directory code execution (CVE-2022-21699)

CVE-2022-21699 lets attackers plant crafted profile files in shared directories, triggering silent code execution when victims launch IPython or Jupyter sessions.

Safeguard Research Team
Research
8 min read

CVE-2022-21699 is a local code-execution vulnerability in IPython, the interactive Python shell that sits underneath Jupyter Notebook, JupyterLab, and a huge share of the data science and machine learning tooling ecosystem. The flaw allows an attacker who can influence the contents of a directory — a shared /tmp, a cloned git repository, a CI runner's workspace, a network home share, or any world-writable path — to plant a crafted profile or startup file that IPython will load and execute automatically the next time a victim launches an interactive session from that location. No malicious notebook cell, no phishing link, no explicit "run this file" action is required beyond the victim starting ipython (or a Jupyter kernel that wraps it) while sitting in the poisoned directory. For teams that run notebooks on shared infrastructure, in CI/CD pipelines, or in containers built from untrusted base images, this turns an otherwise mundane action — opening a Python shell — into an arbitrary code execution primitive.

What Makes This Bug Dangerous

IPython supports "profiles": named collections of configuration and startup scripts that customize a session (extensions to load, variables to pre-populate, banners to display, and so on). Profiles live under a .ipython directory, and IPython also honors profile-like directories and startup scripts discovered relative to the current working directory in certain configurations. Prior to the fix, IPython did not adequately validate the ownership and permissions of these directories, nor did it warn a user before silently executing Python code found in profile_default/startup/ or similar locations. An attacker who can write to a directory a victim is likely to cd into — a shared scratch space, a temp directory with weak permissions, an artifact directory produced by a build pipeline — can drop a startup script there ahead of time. When the victim later opens an IPython or Jupyter session rooted in that directory, the attacker's code runs with the victim's privileges, silently and immediately.

This is the classic "crafted directory" class of vulnerability: the trust boundary is the filesystem path a tool is launched from, not the code the user believes they are running. It's conceptually similar to .pth file abuse, malicious .git hooks, or CWD-relative DLL/.so loading bugs on other platforms — the tool trusts local, ambient files more than it should.

Affected Versions and Components

According to the GitHub Security Advisory for this issue (GHSA-pq7m-3gw7-gq5x) and the corresponding NVD entry, the vulnerability affects:

  • All IPython 7.x releases prior to 7.31.1
  • IPython 8.0.0 specifically in the 8.x line

Fixes landed in IPython 7.31.1 and IPython 8.0.1. Any environment pinned to an older 7.x release, or one that happened to land on the 8.0.0 release before the point fix shipped, should be treated as exposed.

Because IPython is a foundational dependency rather than a top-level application most teams install directly, exposure is often indirect and easy to miss. It ships transitively through:

  • jupyter, jupyterlab, and notebook (all pull in IPython as a kernel dependency)
  • ipykernel, used by VS Code's Jupyter extension, PyCharm, and Google Colab-style local runners
  • Data science stacks and ML platforms that bundle a Jupyter-compatible kernel for interactive experimentation
  • Base container images for ML training/inference that include a full Python data-science toolchain "just in case"

Teams that scan only their direct requirements.txt or pyproject.toml for known-vulnerable packages, without resolving the full transitive dependency graph, frequently miss vulnerable IPython versions pulled in by a notebook server, an internal ML platform, or a vendored data-science image.

Severity, Exploit Probability, and KEV Status

NVD scores CVE-2022-21699 as CVSS 3.1 base score 7.8 (High), with a vector reflecting a local attack surface that requires user interaction but yields high impact across confidentiality, integrity, and availability once triggered (AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H). The "local" attack vector and "user interaction required" component keep this out of the most severe remote-exploitation tier, but the payoff — full code execution in the context of whoever launches the session — is the same as many critical RCE bugs once the precondition (a shared or attacker-writable directory) is met, which is a routine condition in CI runners, multi-tenant notebook servers, and shared development boxes.

EPSS (Exploit Prediction Scoring System) data for this CVE has historically sat in the low range, consistent with a vulnerability that needs a specific environmental setup rather than being remotely, opportunistically exploitable over the internet. It has not appeared on CISA's Known Exploited Vulnerabilities (KEV) catalog as of this writing, and we are not aware of confirmed in-the-wild exploitation. That combination — meaningful impact, low EPSS, no KEV listing — is exactly the profile that causes vulnerabilities like this to sit unpatched for a long time: nothing is on fire, so it slips down the backlog. The risk is concentrated in specific, common environments (shared compute, CI, containers built from stale base images) rather than being broadly internet-facing, which is precisely why supply-chain-aware detection matters more than generic scanning.

Disclosure Timeline

  • Reported to the IPython maintainers: The issue was identified as a directory-trust flaw in IPython's profile and startup-file loading logic and reported through IPython's security disclosure process.
  • Late January 2022: IPython maintainers published patched releases, 7.31.1 and 8.0.1, hardening how profile directories and startup scripts are resolved and executed.
  • Public disclosure: GitHub Security Advisory GHSA-pq7m-3gw7-gq5x was published alongside the CVE assignment, detailing the crafted-directory execution path and confirming the affected version ranges.
  • Downstream propagation: Because IPython is embedded in Jupyter, JupyterLab, and countless ML platform images, downstream projects and container base images needed their own follow-up updates to pull in the patched IPython release — a lag that, in our experience auditing customer environments, can persist for months or years in slow-moving internal images.

Remediation Steps

  1. Upgrade IPython directly. Move 7.x installations to ipython>=7.31.1 and any 8.x installation to ipython>=8.0.1 (or the latest 8.x release, which stays ahead of this fix). Update requirements.txt, pyproject.toml, Pipfile, or conda environment specs accordingly and re-lock.

  2. Chase the transitive dependency, not just the direct one. Audit jupyter, jupyterlab, notebook, ipykernel, and any internal ML platform packages for the IPython version they resolve to at install time — a direct dependency pin doesn't help if a wrapper package still drags in a vulnerable IPython release. Regenerate lockfiles and rebuild container images after bumping the constraint.

  3. Rebuild and re-scan base images. ML training and notebook-serving container images are notorious for going stale. Rebuild any image that bundles Jupyter/IPython, and add a CI gate that fails the build if a known-vulnerable IPython version resolves into the final image layer.

  4. Eliminate shared, writable launch directories. Avoid launching IPython/Jupyter sessions from world-writable or group-writable paths such as /tmp, shared NFS home directories, or CI workspace directories that multiple pipelines or users can write to. Where shared scratch space is unavoidable, isolate it per-job/per-user with restrictive permissions (700 on profile directories, no shared ownership).

  5. Audit existing profile directories for tampering. Check ~/.ipython/profile_default/startup/ (and any custom profile directories) across shared and CI hosts for unexpected .py files that predate the current user's own configuration — this is the artifact an attacker would have planted to exploit the unpatched behavior.

  6. Restrict permissions on .ipython and profile paths going forward. Even on patched versions, defense-in-depth permission hygiene (owner-only read/write/execute on profile directories) reduces the blast radius of any future variant of this bug class.

  7. Add this CVE to your continuous SCA/SBOM monitoring, since IPython's presence is easy to lose track of across ML platform images, notebook servers, and developer laptops that get rebuilt infrequently.

How Safeguard Helps

Safeguard's SBOM generation and ingestion pipeline surfaces IPython wherever it actually lives in your environment — direct dependency, transitive pull-in through Jupyter/ipykernel, or baked into a stale ML container image — so CVE-2022-21699 doesn't hide behind a wrapper package your scanner never resolves. Reachability analysis then tells you whether the vulnerable profile-loading code path is realistically invokable in your specific deployment (an interactive notebook server versus a headless inference container that never launches an IPython shell at all), letting your team prioritize the shared-compute and CI-runner instances that actually carry risk instead of triaging every image that happens to list ipython in its manifest. Griffin AI correlates that reachability signal with how the affected hosts are actually used — multi-tenant notebook servers, CI workspaces with shared scratch directories — to flag the specific environments where the crafted-directory precondition is plausible. When a fix is warranted, Safeguard can open an auto-fix pull request that bumps IPython to a patched release across your dependency manifests and lockfiles, so remediation ships as a reviewable diff instead of a manual hunt through every requirements.txt and container Dockerfile in your fleet.

Never miss an update

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