h5-torch-wrapper-modelscan-bypass-poc / huntr_report_draft.md
captainarp's picture
Add HDF5 TorchModuleWrapper ModelScan bypass PoC
f3aeec0 verified
|
Raw History Blame Contribute Delete
3.5 kB

Summary

This report demonstrates a ModelScan scanner bypass for an HDF5/Keras-H5 model file containing an unsafe TorchModuleWrapper deserialization path.

ModelScan 0.8.8 scans the submitted .h5 file with H5LambdaDetectScan and reports 0 issues and 0 errors. The same H5 model_config contains a keras.layers.TorchModuleWrapper layer with a base64-encoded pickle payload in the module field.

Keras itself treats this path as unsafe: default safe_mode=True blocks it. When unsafe deserialization is explicitly enabled with safe_mode=False, Keras reaches torch.load(..., weights_only=False) and the embedded payload writes a harmless marker file.

Affected Format

HDF5 / Keras H5 .h5

Security Impact

This is a scanner bypass for an unsafe HDF5/Keras-H5 deserialization construct.

If a downstream workflow relies on ModelScan to decide whether a .h5 model file is safe, this payload is reported clean. If the same model is later loaded in a workflow that enables unsafe Keras deserialization, the embedded torch.load() pickle path executes code during model loading.

The PoC payload is intentionally non-destructive. It only writes:

h5_torch_wrapper_marker.txt

with contents:

H5_TORCH_WRAPPER_BYPASS

Reproduction Steps

  1. Download or clone the public Hugging Face PoC repo.

  2. Install dependencies:

pip install -r requirements.txt

  1. Run the verifier:

python verify_poc.py --payload poc_h5_torch_wrapper.h5

  1. Observe that ModelScan reports:

0 issues

0 errors

total_scanned: 1

  1. Observe that default Keras loading blocks the unsafe path:

Marker after safe_mode=True: False

  1. Observe that unsafe deserialization reaches the embedded torch.load() path and writes the marker:

Marker after safe_mode=False: True

Marker contents: H5_TORCH_WRAPPER_BYPASS

Root Cause

ModelScan's H5 scanner checks the H5 model_config for layers whose class_name is Lambda, but it does not detect other unsafe Keras deserialization constructs such as keras.layers.TorchModuleWrapper.

Keras treats TorchModuleWrapper as unsafe because TorchModuleWrapper.from_config() can base64-decode the module field and pass it to:

torch.load(..., weights_only=False)

This can execute attacker-controlled pickle payloads when unsafe deserialization is enabled.

Scope Note

This is not a claim that default Keras loading executes the payload. Default safe_mode=True blocks the unsafe TorchModuleWrapper deserialization path.

The reported issue is that ModelScan reports 0 issues and 0 errors for an HDF5/Keras-H5 file containing an unsafe construct that Keras itself treats as dangerous. ModelScan already flags Lambda layers in H5 model_config, but it does not flag TorchModuleWrapper.

Evidence

PoC file:

poc_h5_torch_wrapper.h5

Verification script:

verify_poc.py

Scanner output:

modelscan_result_h5_torch_wrapper.json

Tested versions:

modelscan==0.8.8

h5py==3.16.0

keras==3.15.0

tensorflow==2.17.1

torch==2.12.1+cpu

Suggested Remediation

ModelScan should extend H5 model_config scanning beyond Lambda layers and flag unsafe Keras deserialization constructs that Keras itself protects with safe mode, including keras.layers.TorchModuleWrapper.

For TorchModuleWrapper, ModelScan should inspect the module field, recognize it as a base64-encoded pickle / torch serialization payload, and either flag it directly or pass it through the existing unsafe pickle scanner.