VYPR
High severityOSV Advisory· Published Jan 10, 2026· Updated Jan 14, 2026

Fickling has Static Analysis Bypass via Incomplete Dangerous Module Blocklist

CVE-2026-22609

Description

Fickling is a Python pickling decompiler and static analyzer. Prior to version 0.1.7, the unsafe_imports() method in Fickling's static analyzer fails to flag several high-risk Python modules that can be used for arbitrary code execution. Malicious pickles importing these modules will not be detected as unsafe, allowing attackers to bypass Fickling's primary static safety checks. This issue has been patched in version 0.1.7.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
ficklingPyPI
< 0.1.70.1.7

Affected products

1

Patches

5
29d5545e74b0

Add importlib, code and multiprocessing to unsafe modules

https://github.com/trailofbits/ficklingThomas ChauchefoinJan 8, 2026via ghsa
2 files changed · +129 0
  • fickling/fickle.py+3 0 modified
    @@ -882,6 +882,9 @@ def unsafe_imports(self) -> Iterator[ast.Import | ast.ImportFrom]:
                     "cProfile",
                     "ctypes",
                     "pydoc",
    +                "importlib",
    +                "code",
    +                "multiprocessing",
                 ):
                     yield node
                 elif "eval" in (n.name for n in node.names):
    
  • test/test_bypasses.py+126 0 modified
    @@ -222,3 +222,129 @@ def test_missing_pydoc(self):
                 res.detailed_results()["AnalysisResult"].get("UnsafeImports"),
                 "from pydoc import locate",
             )
    +
    +    # https://github.com/trailofbits/fickling/security/advisories/GHSA-q5qq-mvfm-j35x
    +    def test_missing_importlib(self):
    +        pickled = Pickled(
    +            [
    +                op.Proto.create(5),
    +                op.ShortBinUnicode("builtins"),
    +                op.Memoize(),
    +                op.ShortBinUnicode("getattr"),
    +                op.Memoize(),
    +                op.StackGlobal(),
    +                op.Memoize(),
    +                op.ShortBinUnicode("importlib"),
    +                op.Memoize(),
    +                op.ShortBinUnicode("import_module"),
    +                op.Memoize(),
    +                op.StackGlobal(),
    +                op.Memoize(),
    +                op.ShortBinUnicode("os"),
    +                op.Memoize(),
    +                op.TupleOne(),
    +                op.Memoize(),
    +                op.Reduce(),
    +                op.Memoize(),
    +                op.ShortBinUnicode("system"),
    +                op.Memoize(),
    +                op.TupleTwo(),
    +                op.Memoize(),
    +                op.Reduce(),
    +                op.Memoize(),
    +                op.ShortBinUnicode("id"),
    +                op.Memoize(),
    +                op.TupleOne(),
    +                op.Memoize(),
    +                op.Reduce(),
    +                op.Memoize(),
    +                op.Stop(),
    +            ]
    +        )
    +        res = check_safety(pickled)
    +        self.assertGreater(res.severity, Severity.LIKELY_SAFE)
    +        self.assertEqual(
    +            res.detailed_results()["AnalysisResult"].get("UnsafeImports"),
    +            "from importlib import import_module",
    +        )
    +
    +    # https://github.com/trailofbits/fickling/security/advisories/GHSA-q5qq-mvfm-j35x
    +    def test_missing_code(self):
    +        pickled = Pickled(
    +            [
    +                op.Proto.create(5),
    +                op.ShortBinUnicode("builtins"),
    +                op.Memoize(),
    +                op.ShortBinUnicode("getattr"),
    +                op.Memoize(),
    +                op.StackGlobal(),
    +                op.Memoize(),
    +                op.ShortBinUnicode("code"),
    +                op.Memoize(),
    +                op.ShortBinUnicode("InteractiveInterpreter"),
    +                op.Memoize(),
    +                op.StackGlobal(),
    +                op.Memoize(),
    +                op.EmptyTuple(),
    +                op.Memoize(),
    +                op.Reduce(),
    +                op.Memoize(),
    +                op.ShortBinUnicode("runsource"),
    +                op.Memoize(),
    +                op.TupleTwo(),
    +                op.Memoize(),
    +                op.Reduce(),
    +                op.Memoize(),
    +                op.ShortBinUnicode('import os; os.system("id")'),
    +                op.Memoize(),
    +                op.TupleOne(),
    +                op.Memoize(),
    +                op.Reduce(),
    +                op.Memoize(),
    +                op.Stop(),
    +            ]
    +        )
    +        res = check_safety(pickled)
    +        self.assertGreater(res.severity, Severity.LIKELY_SAFE)
    +        self.assertEqual(
    +            res.detailed_results()["AnalysisResult"].get("UnsafeImports"),
    +            "from code import InteractiveInterpreter",
    +        )
    +
    +    # https://github.com/trailofbits/fickling/security/advisories/GHSA-q5qq-mvfm-j35x
    +    def test_missing_multiprocessing(self):
    +        pickled = Pickled(
    +            [
    +                op.Proto.create(5),
    +                op.Frame(74),
    +                op.ShortBinUnicode("multiprocessing.util"),
    +                op.Memoize(),
    +                op.ShortBinUnicode("spawnv_passfds"),
    +                op.Memoize(),
    +                op.StackGlobal(),
    +                op.Memoize(),
    +                op.ShortBinBytes(b"/bin/sh"),
    +                op.Memoize(),
    +                op.EmptyList(),
    +                op.Memoize(),
    +                op.Mark(),
    +                op.BinGet(3),
    +                op.ShortBinBytes(b"-c"),
    +                op.Memoize(),
    +                op.ShortBinBytes(b"id"),
    +                op.Memoize(),
    +                op.Appends(),
    +                op.EmptyTuple(),
    +                op.TupleThree(),
    +                op.Memoize(),
    +                op.Reduce(),
    +                op.Memoize(),
    +                op.Stop(),
    +            ]
    +        )
    +        res = check_safety(pickled)
    +        self.assertGreater(res.severity, Severity.LIKELY_SAFE)
    +        self.assertEqual(
    +            res.detailed_results()["AnalysisResult"].get("UnsafeImports"),
    +            "from multiprocessing.util import spawnv_passfds",
    +        )
    
eb299b453342

Check the root module name during unsafe import detection

https://github.com/trailofbits/ficklingThomas ChauchefoinJan 8, 2026via ghsa
1 file changed · +2 2
  • fickling/fickle.py+2 2 modified
    @@ -864,7 +864,7 @@ def is_likely_safe(self):
     
         def unsafe_imports(self) -> Iterator[ast.Import | ast.ImportFrom]:
             for node in self.properties.imports:
    -            if node.module in (
    +            if node.module and node.module.split(".")[0] in (
                     "__builtin__",
                     "__builtins__",
                     "builtins",
    @@ -1157,7 +1157,7 @@ def run(self, interpreter: Interpreter):
                     f"Module: {type(module).__name__}, Attr: {type(attr).__name__}"
                 )
     
    -        if not module.isidentifier() or not attr.isidentifier():
    +        if not all(m.isidentifier() for m in module.split(".")) or not attr.isidentifier():
                 raise ValueError(
                     f"Extracted identifiers are not valid Python identifiers. "
                     f"Module: {module!r}, Attr: {attr!r}"
    
6b400e1a2525

Add missing reference to GHSA-q5qq-mvfm-j35x in test_missing_runpy()

https://github.com/trailofbits/ficklingThomas ChauchefoinJan 7, 2026via ghsa
1 file changed · +1 0
  • test/test_bypasses.py+1 0 modified
    @@ -85,6 +85,7 @@ def test_missing_marshal_and_types(self):
             self.assertGreater(check_safety(opcodes).severity, Severity.LIKELY_SAFE)
     
         # https://github.com/trailofbits/fickling/security/advisories/GHSA-wfq2-52f7-7qvj
    +    # https://github.com/trailofbits/fickling/security/advisories/GHSA-q5qq-mvfm-j35x
         def test_missing_runpy(self):
             pickled = Pickled(
                 [
    
b793563e60a5

Add pydoc and ctypes to unsafe imports

https://github.com/trailofbits/ficklingThomas ChauchefoinJan 7, 2026via ghsa
2 files changed · +84 0
  • fickling/fickle.py+2 0 modified
    @@ -880,6 +880,8 @@ def unsafe_imports(self) -> Iterator[ast.Import | ast.ImportFrom]:
                     "types",
                     "runpy",
                     "cProfile",
    +                "ctypes",
    +                "pydoc",
                 ):
                     yield node
                 elif "eval" in (n.name for n in node.names):
    
  • test/test_bypasses.py+82 0 modified
    @@ -139,3 +139,85 @@ def test_missing_cprofile(self):
                 res.detailed_results()["AnalysisResult"].get("UnsafeImports"),
                 "from cProfile import run",
             )
    +
    +    # https://github.com/trailofbits/fickling/security/advisories/GHSA-q5qq-mvfm-j35x
    +    # https://github.com/trailofbits/fickling/security/advisories/GHSA-5hvc-6wx8-mvv4
    +    def test_missing_ctypes(self):
    +        pickled = Pickled(
    +            [
    +                op.Proto.create(5),
    +                op.ShortBinUnicode("builtins"),
    +                op.Memoize(),
    +                op.ShortBinUnicode("getattr"),
    +                op.Memoize(),
    +                op.StackGlobal(),
    +                op.Memoize(),
    +                op.ShortBinUnicode("ctypes"),
    +                op.Memoize(),
    +                op.ShortBinUnicode("CDLL"),
    +                op.Memoize(),
    +                op.StackGlobal(),
    +                op.Memoize(),
    +                op.ShortBinUnicode("libc.dylib"),
    +                op.Memoize(),
    +                op.TupleOne(),
    +                op.Memoize(),
    +                op.Reduce(),
    +                op.Memoize(),
    +                op.ShortBinUnicode("system"),
    +                op.Memoize(),
    +                op.TupleTwo(),
    +                op.Memoize(),
    +                op.Reduce(),
    +                op.Memoize(),
    +                op.ShortBinBytes(b"id"),
    +                op.Memoize(),
    +                op.TupleOne(),
    +                op.Memoize(),
    +                op.Reduce(),
    +                op.Memoize(),
    +                op.Stop(),
    +            ]
    +        )
    +        res = check_safety(pickled)
    +        self.assertGreater(res.severity, Severity.LIKELY_SAFE)
    +        self.assertEqual(
    +            res.detailed_results()["AnalysisResult"].get("UnsafeImports"),
    +            "from ctypes import CDLL",
    +        )
    +
    +    # https://github.com/trailofbits/fickling/security/advisories/GHSA-5hvc-6wx8-mvv4
    +    def test_missing_pydoc(self):
    +        pickled = Pickled(
    +            [
    +                op.Global("pydoc locate"),
    +                op.String("ctypes.windll.kernel32.WinExec"),
    +                op.TupleOne(),
    +                op.Reduce(),
    +                op.Put(0),
    +                op.Pop(),
    +                op.Get(0),
    +                op.ShortBinBytes(b"calc.exe"),
    +                op.BinInt1(1),
    +                op.TupleTwo(),
    +                op.Reduce(),
    +                op.Put(1),
    +                op.Pop(),
    +                op.Global("builtins Exception"),
    +                op.EmptyTuple(),
    +                op.Reduce(),
    +                op.Put(2),
    +                op.EmptyDict(),
    +                op.String("rce_status"),
    +                op.Get(1),
    +                op.SetItem(),
    +                op.Build(),
    +                op.Stop(),
    +            ]
    +        )
    +        res = check_safety(pickled)
    +        self.assertGreater(res.severity, Severity.LIKELY_SAFE)
    +        self.assertEqual(
    +            res.detailed_results()["AnalysisResult"].get("UnsafeImports"),
    +            "from pydoc import locate",
    +        )
    
9a2b3f89bd05

Add runpy to unsafe modules

https://github.com/trailofbits/ficklingThomas ChauchefoinJan 7, 2026via ghsa
2 files changed · +29 0
  • fickling/fickle.py+1 0 modified
    @@ -878,6 +878,7 @@ def unsafe_imports(self) -> Iterator[ast.Import | ast.ImportFrom]:
                     "pty",
                     "marshal",
                     "types",
    +                "runpy",
                 ):
                     yield node
                 elif "eval" in (n.name for n in node.names):
    
  • test/test_bypasses.py+28 0 modified
    @@ -83,3 +83,31 @@ def test_missing_marshal_and_types(self):
             )
     
             self.assertGreater(check_safety(opcodes).severity, Severity.LIKELY_SAFE)
    +
    +    # https://github.com/trailofbits/fickling/security/advisories/GHSA-wfq2-52f7-7qvj
    +    def test_missing_runpy(self):
    +        pickled = Pickled(
    +            [
    +                op.Proto.create(5),
    +                op.Frame(46),
    +                op.ShortBinUnicode("runpy"),
    +                op.Memoize(),
    +                op.ShortBinUnicode("run_path"),
    +                op.Memoize(),
    +                op.StackGlobal(),
    +                op.Memoize(),
    +                op.ShortBinUnicode("/tmp/malicious.py"),
    +                op.Memoize(),
    +                op.TupleOne(),
    +                op.Memoize(),
    +                op.Reduce(),
    +                op.Memoize(),
    +                op.Stop(),
    +            ]
    +        )
    +        res = check_safety(pickled)
    +        self.assertGreater(res.severity, Severity.LIKELY_SAFE)
    +        self.assertEqual(
    +            res.detailed_results()["AnalysisResult"].get("UnsafeImports"),
    +            "from runpy import run_path",
    +        )
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

11

News mentions

0

No linked articles in our index yet.