# Pickle scanner bypass via OpenCV `cv2.imwrite` writes attacker-controlled PNG while picklescan and modelscan report clean ## Summary A crafted pickle can call `cv2.imwrite()` during normal `pickle.load()` and write an attacker-controlled PNG file, while both current scanners report the file clean. The payload uses raw protocol-4 `STACK_GLOBAL` references for: - `numpy.array` - `cv2.imwrite` It first builds a tiny NumPy pixel array, then calls `cv2.imwrite('/tmp/mfv_cv2_imwrite_marker.png', array)`. This avoids blocked globals such as `builtins.getattr`, `os.system`, `open`, `eval`, and `exec`. ## Target - Huntr MFV target: `Pickle` - Scanner versions: `picklescan==1.0.4`, `modelscan==0.8.8` - Load proof runtime: Python 3.12 with `opencv-python`/`cv2 4.13.0` and NumPy installed - Artifact: `malicious_cv2_imwrite.pkl` - Public PoC package: https://huggingface.co/hacnho/opencv-imwrite-scanner-bypass-poc - Huntr submission: https://huntr.com/bounties/48b4ec56-5832-420e-9edc-f95a2b5ee6b6 ## Proof Of Concept Run: ```bash python3 reproduce_cv2_imwrite_scanner_bypass.py ``` The script regenerates the pickle, runs both scanners, then loads the pickle and verifies the PNG marker. Expected proof: ```json "load_result": true, "marker": "/tmp/mfv_cv2_imwrite_marker.png", "marker_exists": true, "marker_magic": "89504e470d0a1a0a" ``` ## Scanner Output `picklescan`: ```text ----------- SCAN SUMMARY ----------- Scanned files: 1 Infected files: 0 Dangerous globals: 0 ``` `modelscan`: ```text Scanning .../malicious_cv2_imwrite.pkl using modelscan.scanners.PickleUnsafeOpScan model scan --- Summary --- No issues found! 🎉 ``` Both scanners exit `0`. ## Impact Any application that relies on these scanners to pre-screen pickle artifacts can be made to accept a pickle that writes an attacker-controlled image file during ordinary `pickle.load()`, provided the loading environment has OpenCV installed. The demonstrated impact is arbitrary file creation/overwrite with image content through OpenCV's image encoder. This can be used to place attacker-controlled files at application-chosen writable paths, poison generated assets, or overwrite files consumed later by the application. I am not claiming arbitrary command execution for this specific gadget. The honest precondition is that OpenCV (`cv2`) is importable in the consumer environment. ## Root Cause The scanners' unsafe-global policies do not include OpenCV image writer callables such as `cv2.imwrite`. The pickle global sequence is visible to the scanners, but neither scanner classifies `cv2.imwrite` as dangerous even though it has filesystem side effects during deserialization. The dangerous behavior is not hidden behind `getattr` or a nested pickle. It is a direct file-writing callable reached by `REDUCE`. ## Suggested Fix Add OpenCV file-writing APIs to the unsafe-global/blocklist policy, at minimum: - `cv2.imwrite` - `cv2.imwritemulti` - `cv2.FileStorage` Scanner tests should include a pickle that invokes `cv2.imwrite(path, numpy.array(...))` and assert that it is reported as dangerous. ## Deduplication Checked the local submitted-report denylist built from `/home/hacnho/Projects/research/huntr_mysubmission.html` plus 5 manual dashboard extras. It contains 130 submitted Huntr reports as of 2026-06-24 and has no existing report for: - `cv2.imwrite` - `OpenCV imwrite` - `opencv imwrite` - `Pickle scanner bypass via OpenCV` I also checked live Huntr hacktivity on 2026-06-24 with these terms: - `cv2.imwrite`: 400 reports scanned, 0 matching report links - `OpenCV imwrite`: 698 reports scanned, 0 matching report links - `opencv imwrite`: 300 reports scanned, 0 matching report links - `cv2 imwrite pickle`: 300 reports scanned, 0 matching report links - `Pickle OpenCV imwrite`: 500 reports scanned, 0 matching report links Related submitted reports cover different image/file writer gadgets such as PIL and matplotlib, plus an OpenCV Caffe parser crash. This report is a distinct pickle scanner-bypass gadget: OpenCV's exported `cv2.imwrite` file writer.