google / google/deluca

Unsafe pickle deserialization in deluca load paths enables arbitrary code execution (CWE-502)

Open
#91 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Jupyter Notebook
Stars
126
Forks
17
PR merge metrics
No merged PRs in 30d

Description

Multiple code paths in `deluca` pass attacker-controlled file contents to `pickle.load()`. Python's `pickle` is not a safe data format: during deserialization it can execute arbitrary Python code via mechanisms such as `__reduce__` and `__setstate__`.

As a result, loading a malicious `.pkl` file through any of the affected APIs results in arbitrary code execution in the context of the user running the Python process.

## Affected code paths

- `deluca/core.py:44` - `deluca.load(path)`
- `deluca/lung/utils/data/analyzer.py:34` - `Analyzer(path)`
- `deluca/lung/utils/data/breath_dataset.py:35` - `BreathDataset.from_paths([...])`
- `deluca/lung/utils/scripts/test_simulator.py:42` - `test_simulator(..., dataset="artifact.pkl")`

Tested against commit `e483d2f13cd0ee15784ac3f34b82bfeecc47f115`.

## Impact

An attacker who can deliver a malicious pickle artifact (shared dataset, checkpoint, benchmark file, saved experiment result, Hugging Face–style artifact, etc.) to a Deluca user achieves arbitrary code execution as that user the moment the file is loaded through a normal workflow.

No prior access to the victim host, authentication, network exposure of a Deluca service, or elevated privileges is required.

In ML and research environments - where serialized datasets, checkpoints, and experiment outputs are routinely exchanged between collaborators and pulled from third-party sources this attack scenario is realistic. Post-exploitation consequences include reading local files, exfiltrating SSH keys / API tokens / cloud credentials, tampering with experiments or model artifacts, and pivoting into shared research infrastructure or CI pipelines.

## Root cause

Each affected call site invokes `pickle.load()` (or equivalent) directly on a user-supplied path with no allow-list of permitted classes, no restricted unpickler, and no warning to the caller that the operation is unsafe.

## Suggested fix

A minimal, merge-friendly mitigation:

1. Centralise pickle loading in a single helper in `deluca/core.py`.
2. Have the helper emit a `UserWarning` explaining that the operation is unsafe, unless the caller explicitly passes `trust_source=True`.
3. Update the four affected call sites to go through the helper and propagate the `trust_source` flag.
4. Document the risk in the public API docstrings so downstream users understand that untrusted artifacts must not be loaded.

A more thorough fix - replacing pickle with a safe serialization format such as `safetensors` + JSON for model and dataset state - would be preferable but breaks backwards compatibility with existing Deluca checkpoints, so is intentionally out of scope for this initial patch.

A PR implementing the minimal mitigation will be opened and linked here.

## Reproduction

A safe, non-destructive proof-of-concept is available on request and will be included as a regression test in the linked PR. The PoC uses `print()` as the payload - no filesystem, network, or process side effects.

## References

- CWE-502: Deserialization of Untrusted Data
- Python docs - `pickle` module security warning: https://docs.python.org/3/library/pickle.html

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.