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

Fickling vulnerable to detection bypass due to "builtins" blindness

CVE-2026-22612

Description

Fickling is a Python pickling decompiler and static analyzer. Prior to version 0.1.7, Fickling is vulnerable to detection bypass due to "builtins" blindness. 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

1
9f309ab83479

Emit AST nodes for builtins imports

https://github.com/trailofbits/ficklingThomas ChauchefoinJan 9, 2026via ghsa
3 files changed · +39 21
  • fickling/fickle.py+6 19 modified
    @@ -1110,12 +1110,8 @@ def attr(self) -> str:
     
         def run(self, interpreter: Interpreter):
             module, attr = self.module, self.attr
    -        if module in ("__builtin__", "__builtins__", "builtins"):
    -            # no need to emit an import for builtins!
    -            pass
    -        else:
    -            alias = ast.alias(attr)
    -            interpreter.module_body.append(ast.ImportFrom(module=module, names=[alias], level=0))
    +        alias = ast.alias(attr)
    +        interpreter.module_body.append(ast.ImportFrom(module=module, names=[alias], level=0))
             interpreter.stack.append(ast.Name(attr, ast.Load()))
     
         def encode(self) -> bytes:
    @@ -1166,13 +1162,8 @@ def run(self, interpreter: Interpreter):
                     f"Module: {module!r}, Attr: {attr!r}"
                 )
     
    -        # Continue with normal processing
    -        if module in ("__builtin__", "__builtins__", "builtins"):
    -            # no need to emit an import for builtins!
    -            pass
    -        else:
    -            alias = ast.alias(attr)
    -            interpreter.module_body.append(ast.ImportFrom(module=module, names=[alias], level=0))
    +        alias = ast.alias(attr)
    +        interpreter.module_body.append(ast.ImportFrom(module=module, names=[alias], level=0))
             interpreter.stack.append(ast.Name(attr, ast.Load()))
     
     
    @@ -1194,12 +1185,8 @@ def cls(self) -> str:
     
         def run(self, interpreter: Interpreter, stack_slice: List[ast.expr]):
             module, classname = self.module, self.cls
    -        if module in ("__builtin__", "__builtins__", "builtins"):
    -            # no need to emit an import for builtins!
    -            pass
    -        else:
    -            alias = ast.alias(classname)
    -            interpreter.module_body.append(ast.ImportFrom(module=module, names=[alias], level=0))
    +        alias = ast.alias(classname)
    +        interpreter.module_body.append(ast.ImportFrom(module=module, names=[alias], level=0))
             args = ast.Tuple(tuple(stack_slice))
             call = ast.Call(ast.Name(classname, ast.Load()), list(args.elts), [])
             var_name = interpreter.new_variable(call)
    
  • test/test_bypasses.py+31 0 modified
    @@ -345,3 +345,34 @@ def test_missing_multiprocessing(self):
                 res.detailed_results()["AnalysisResult"].get("UnsafeImports"),
                 "from multiprocessing.util import spawnv_passfds",
             )
    +
    +    # https://github.com/trailofbits/fickling/security/advisories/GHSA-h4rm-mm56-xf63
    +    def test_builtins_import_bypass(self):
    +        pickled = Pickled(
    +            [
    +                op.Global("builtins __import__"),
    +                op.String("os"),
    +                op.TupleOne(),
    +                op.Reduce(),
    +                op.Put(0),
    +                op.Pop(),
    +                op.Global("builtins getattr"),
    +                op.Get(0),
    +                op.String("system"),
    +                op.TupleTwo(),
    +                op.Reduce(),
    +                op.Put(1),
    +                op.Pop(),
    +                op.Get(1),
    +                op.String("whoami"),
    +                op.TupleOne(),
    +                op.Reduce(),
    +                op.Stop(),
    +            ]
    +        )
    +        res = check_safety(pickled)
    +        self.assertGreater(res.severity, Severity.LIKELY_SAFE)
    +        self.assertEqual(
    +            res.detailed_results()["AnalysisResult"].get("UnsafeImports"),
    +            "from builtins import getattr",
    +        )
    
  • test/test_pickle.py+2 2 modified
    @@ -87,7 +87,7 @@ def test_bytes(self):
         def test_call(self):
             pickled = Pickled(
                 [
    -                fpickle.Global.create("__builtins__", "eval"),
    +                fpickle.Global.create("builtins", "eval"),
                     fpickle.Mark(),
                     fpickle.Unicode("(lambda:1234)()"),
                     fpickle.Tuple(),
    @@ -102,7 +102,7 @@ def test_inst(self):
                 [
                     fpickle.Mark(),
                     fpickle.Unicode("1234"),
    -                fpickle.Inst.create("__builtins__", "int"),
    +                fpickle.Inst.create("builtins", "int"),
                     fpickle.Stop(),
                 ]
             )
    

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

7

News mentions

0

No linked articles in our index yet.