Skip to content

Add transformers.dynamic_module_utils to CRITICAL unsafe_globals blocklist#360

Open
SSJCorpSec wants to merge 1 commit into
protectai:mainfrom
SSJCorpSec:fix/transformers-dynamic-module-unsafe-global
Open

Add transformers.dynamic_module_utils to CRITICAL unsafe_globals blocklist#360
SSJCorpSec wants to merge 1 commit into
protectai:mainfrom
SSJCorpSec:fix/transformers-dynamic-module-unsafe-global

Conversation

@SSJCorpSec

@SSJCorpSec SSJCorpSec commented Jun 25, 2026

Copy link
Copy Markdown

Summary

transformers.dynamic_module_utils.get_class_from_dynamic_module downloads and executes arbitrary Python from a HuggingFace Hub repository (or a local path) at unpickle time. Because no transformers.* symbol appeared in the unsafe_globals blocklist, a pickle gadget using either the GLOBAL opcode (protocol v2) or the STACK_GLOBAL opcode (protocol v4) passed ModelScan with 0 issues while achieving full RCE on joblib.load, pickle.load, and torch.load(weights_only=False).

Disclosed in huntr report ac7bd4e9 (validated, fix bounty open).

Changes

  • modelscan/settings.py: adds "transformers.dynamic_module_utils": "*" to the CRITICAL tier. Wildcard covers get_class_from_dynamic_module and all other callables in the module that share the same download-and-execute behavior.
  • tests/test_modelscan.py: adds malicious_transformers_gen_v2() (GLOBAL opcode, protocol 2) and malicious_transformers_gen_v4() (STACK_GLOBAL opcode, protocol 4) generators, a parametrized fixture initialization for both, and test_scan_pickle_transformers_gadget asserting both are detected as CRITICAL with the correct module and operator fields. No transformers package import required in the test suite.

Why CRITICAL (not HIGH)

The function's own docstring says "It should therefore only be called on trusted repos." When invoked from a pickle gadget it bypasses the trust_remote_code consent gate entirely, making it equivalent in impact to os.system / subprocess.run.

Test

pytest tests/test_modelscan.py::test_scan_pickle_transformers_gadget -v

Expected: 2 parametrized cases, each producing 1 CRITICAL issue with module=transformers.dynamic_module_utils, operator=get_class_from_dynamic_module.

get_class_from_dynamic_module downloads and executes arbitrary Python
from a HuggingFace Hub repo (or local path) at unpickle time. Because
the module was absent from the blocklist, a STACK_GLOBAL/GLOBAL gadget
passed ModelScan with 0 issues while achieving full RCE on joblib.load,
pickle.load, and torch.load(weights_only=False).

Wildcard covers all callables in the module. Tests cover both the GLOBAL
opcode path (protocol v2) and the STACK_GLOBAL path (protocol v4) so
neither scanner branch is left unverified.

Fixes huntr report ac7bd4e9-3b8b-41e3-bc8b-b1b19b2427d4.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@SSJCorpSec SSJCorpSec force-pushed the fix/transformers-dynamic-module-unsafe-global branch from fc52b46 to ea21609 Compare June 25, 2026 06:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant