# ModelScan 0.8.x ACE bypass via timeit.timeit — unblocked stdlib module Proof of concept for the Protect AI / huntr.com Model Format Vulnerability program. ## Vulnerability ModelScan 0.8.8 maintains an unsafe_globals blocklist to detect dangerous pickle payloads. The timeit module is completely absent from this blocklist at every severity level (CRITICAL, HIGH, MEDIUM, LOW). timeit.timeit(stmt, setup) compiles and executes the setup argument exactly once as Python code via compile() and exec() internally, then runs stmt N times for timing purposes. Placing a malicious command in setup achieves single-execution arbitrary code execution while stmt='pass' runs harmlessly in the background. The payload contains a single GLOBAL reference: ('timeit', 'timeit'). This is not present in unsafe_globals. ModelScan reports clean. ## Affected - Scanner: ModelScan 0.8.8 (latest as of July 2026) - Extensions: .pkl .pickle .joblib .dill .dat .data - Trigger: pickle.loads() and joblib.load() ## Reproduction Install requirements: pip install modelscan joblib Generate the PoC files: python3 poc_timeit_ace.py This creates poc_timeit_bypass.pkl and poc_timeit_bypass.joblib and confirms that os.system() executes at deserialization time. Scan with ModelScan: modelscan -p poc_timeit_bypass.pkl Expected output: No issues found The indicator file /tmp/ml_pwned_timeit was already created by the PoC script, demonstrating that execution happened before the scan result was even printed. ## Payload Pickle protocol 4 bytecode: SHORT_BINUNICODE 'timeit' MEMOIZE(0) SHORT_BINUNICODE 'timeit' MEMOIZE(1) STACK_GLOBAL MEMOIZE(2) <- timeit.timeit function SHORT_BINUNICODE 'pass' MEMOIZE(3) <- stmt: harmless, runs 1M times SHORT_BINUNICODE 'import os...' MEMOIZE(4) <- setup: executes once -> RCE TUPLE2 MEMOIZE(5) REDUCE <- timeit.timeit('pass', payload) STOP Total payload size: 93 bytes. ## Root Cause unsafe_globals in modelscan/settings.py does not include the timeit module. The blocklist was built around well-known dangerous modules (os, subprocess, sys, socket, etc.) but does not account for stdlib modules that indirectly execute code strings via compile()+exec(), such as timeit, doctest, and trace. ## Fix Add to unsafe_globals CRITICAL: "timeit": "*", ## Files - poc_timeit_ace.py: generates and verifies both PoC files - poc_timeit_bypass.pkl: malicious pickle, passes ModelScan, executes os.system() - poc_timeit_bypass.joblib: same payload with .joblib extension ## Ethics Payload uses a benign indicator (id > /tmp/ml_pwned_timeit). Submitted exclusively through the Protect AI / huntr.com bug bounty program.