# Rails Active Storage GHSA-f3w6-w445-fwh9: arbitrary file write and read via ImageMagick coders

Defensive threat-hunting and exposure-check pack. Built 2026-11-04 (Europe/London, GMT). No exploit
payloads are included and none should be added.

## Verified facts (primary sources, checked 2026-11-04)

| Field | Value | Source |
|---|---|---|
| Identifier | GHSA-f3w6-w445-fwh9, "Possible arbitrary file write and read in Active Storage image analysis" | Rails GitHub advisory |
| CVE | None assigned. Advisory `cve_id` is null; OSV returns "not found"; NVD keyword search for the GHSA returns 0 results | GitHub advisory API, OSV API, NVD API |
| Severity | Critical, CVSS 9.8, vector `CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H` (CVSS 3.0, not 3.1) | Rails GitHub advisory |
| CWE | CWE-1188 (Initialization of a Resource with an Insecure Default) | Rails GitHub advisory |
| Published | 2026-11-04 03:09 GMT | Rails GitHub advisory |
| Affected | `activestorage` < 8.0.5.2, and >= 8.1, < 8.1.4.1 | Rails GitHub advisory |
| Fixed | 8.0.5.2 and 8.1.4.1 (RubyGems: 8.0.5.2 published 2026-11-04 02:34 GMT, 8.1.4.1 published 02:47 GMT) | Rails GitHub advisory, RubyGems versions API |
| Patches | 8.0: commit ea8c40121ce53a3d0255cd4eb6d13bfe3bc01776; 8.1: commit 468aa9a93fc98f5476b6821770f4ccfcb7f7d328 | Rails GitHub advisory, fix commit |
| Attack | Unauthenticated upload of a crafted file. The fix commit states an uploaded SVG reached the MSL coder through the ImageMagick analyzer at attach time. Attaching is sufficient; no variant, preview or `analyze` call needed. Rails "do not assume it is the only" chain | Advisory, fix commit |
| Impact | Arbitrary file write; with the Disk service, read of files readable by the app process (typically `secret_key_base` and credentials), which "may in turn allow" RCE or lateral movement | Rails GitHub advisory |
| Exposure (all must hold) | 1) ImageMagick used for Active Storage (`variant_processor = :mini_magick`, default for `load_defaults 6.1` or earlier); 2) ImageMagick policy does not already deny the coders (Debian and Ubuntu packaged policies do not); 3) uploads from untrusted users | Rails GitHub advisory |
| Defaults check | Confirmed in Rails source: `load_defaults "7.0"` sets `active_storage.variant_processor = :vips`; engine falls back to `:mini_magick` when unset. Note: in activestorage 6.x `ImageAnalyzer` always uses MiniMagick regardless of `variant_processor` | railties `configuration.rb` and activestorage `engine.rb` at v8.0.5.2; `image_analyzer.rb` at v6.1.7.10 and v7.0.8.7 |
| Fix contents | Ships `activestorage/lib/active_storage/image_magick/policy.xml`, prepended to `MAGICK_CONFIGURE_PATH` via `MiniMagick.cli_env` for every MiniMagick process. Requires mini_magick >= 5.1 (older raises at boot with `:mini_magick`) | Fix commit, advisory |
| Policy denies | Coders MSL, EPHEMERAL, TEXT, MPC, SHOW, WIN, PLT, URL, HTTP, HTTPS, FTP, FILE, PS, PS2, PS3, EPS, EPS2, EPS3, EPI, EPSF, EPSI, EPT, EPT2, EPT3, PDF, AI, EPDF, PDFA, POCKETMOD, XPS, PCL; delegate `gs`; path `@*` | `policy.xml` in fix commit |
| Release | Rails 8.0.5.2 and 8.1.4.1 fix 21 security issues, including GHSA-v924-7rp8-4mxh (High, 8.8) | Rails release blog post, rails/rails advisories list (21 advisories published 2026-11-04) |
| EOL | Only 8.1.x and 8.0.x are listed as supported. Rails below 8.0 is unsupported and has no fixed release. Security support for 8.0 ends 2026-11-07 | rubyonrails.org/maintenance, release blog post |
| KEV | Not listed (no CVE exists to list). CISA KEV catalog version 2026.11.03 (released 2026-11-03 20:00 GMT, 1767 entries) | CISA KEV JSON feed |
| Exploitation | No primary source reports exploitation in the wild | Advisory, KEV |
| Credit | Reported by security researchers | Rails GitHub advisory |

### Related: GHSA-v924-7rp8-4mxh

High, CVSS 8.8, `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H` (requires low privileges, unlike f3w6), CWE-646, no CVE.
Same affected and fixed versions. Tempfile extension let the uploader choose FFmpeg/ImageMagick decoders;
AviSynth (`.avs`) in FFmpeg builds with `--enable-avisynth` can disclose files or load a native library.
Switching to `:vips` does NOT address it. Workaround: remove VideoPreviewer, VideoAnalyzer and AudioAnalyzer.

## Corrections to the initial report

| Teammate claim | Finding |
|---|---|
| CVSS 9.8 Critical from Rails | Correct. Vector is CVSS 3.0 (`CVSS:3.0/...`), not 3.1 |
| No CVE yet | Correct as of 2026-11-04 (GitHub, OSV, NVD) |
| e.g. an SVG reaching MSL through the analyzer | Correct (fix commit message). Advisory adds that other chains may exist |
| Attacker "can write any file" | Advisory says arbitrary file write "on the server"; in practice bounded by the app process's permissions (inference, unverified) |
| Disk service read leads to RCE | Advisory says "may in turn allow" RCE or lateral movement. Not certain |
| Affected < 8.0.5.2 and 8.1 < 8.1.4.1 | Correct |
| Default with load_defaults <= 6.1 | Correct (Rails source). Also, activestorage 6.x analyzes with MiniMagick even when `:vips` is set, so `:vips` is not a mitigation there |
| Debian/Ubuntu policies don't block | Correct per advisory |
| Rails < 8.0 EOL with no fix | Correct. Also: 8.0 security support ends 2026-11-07, three days after release |
| Fix adds a restrictive policy | Correct, and it also requires mini_magick >= 5.1. Policy also denies Ghostscript coders, `gs` delegate, URL/FILE coders and `@` path indirection, so PDF/PS processing via ImageMagick breaks unless re-allowed |
| 21 issues incl. GHSA-v924-7rp8-4mxh (8.8) | Correct. v924 needs low privileges (PR:L), and is not mitigated by `:vips` |
| Not in KEV | Correct, catalog 2026.11.03. Exploitation: no primary report |
| Missing from report | Disk-service users must rotate `secret_key_base`, master key, all credentials (advisory "Mitigation"). GraphicsMagick does not read ImageMagick policies. Apps overriding `MiniMagick.cli_env`, `cli_prefix` or `MiniMagick::Shell` do not get the shipped policy |

## Unverified (label carried into queries)

- Process tree (ruby/puma spawning `convert`/`magick`/`identify`) and which ImageMagick binary MiniMagick calls on your build.
- Any specific file-write targets (cron, `~/.ssh`, app code, web root) and any post-exploitation shell activity.
- Whether Defender for Endpoint on Linux records ImageMagick file reads of `config/master.key` (it generally does not log reads; use auditd).
- IOCs: none are primary-published. This pack contains none.
- The KQL has NOT been run against a live tenant.

## Files

| Path | Purpose |
|---|---|
| `kql/rails-activestorage-ghsa-f3w6-w445-fwh9-hunting.kql` | Defender XDR / Sentinel hunts Q1 to Q7 (ImageMagick from ruby, writes outside storage, sensitive-path writes, shells, secret access, SVG/MSL/MVG in storage, TVM inventory) |
| `sigma/*.yml` | 5 Linux Sigma rules (process_creation, file_event, file_access) |
| `sigma/converted/kusto_*.txt` | Kusto conversions: no pipeline, microsoft_xdr, sentinel_asim, azure_monitor |
| `scripts/check-rails-activestorage.sh` | Read-only offline checker (bash) |
| `scripts/Check-RailsActiveStorage.ps1` | Read-only offline checker (PowerShell 7), same logic and output |
| `tests/` | Mocked fixtures, bash tests, Pester tests, `run_all.sh` |

## Checkers

```bash
bash scripts/check-rails-activestorage.sh --app /srv/myapp --policy /etc/ImageMagick-6/policy.xml --storage /srv/myapp/storage
bash scripts/check-rails-activestorage.sh --csv inventory.csv     # host,activestorage_version,variant_processor,policy
pwsh -File scripts/Check-RailsActiveStorage.ps1 -App /srv/myapp -Policy /etc/ImageMagick-7/policy.xml
```

Reads `Gemfile.lock` (activestorage, mini_magick), `config/application.rb` (`load_defaults`), and
`config/application.rb`, `config/environments/*.rb`, `config/initializers/*.rb` (`variant_processor`).
Policy check: NOT EXPOSED only if the policy denies MSL, all Ghostscript coders from the advisory
workaround and the `gs` delegate (or `*`). The optional storage listing content-sniffs the first 512
bytes of each file for SVG, MSL-like and MVG-like content and only lists them.

| Verdict | Exit | Meaning |
|---|---|---|
| PATCHED | 0 | activestorage outside affected ranges (warns if mini_magick < 5.1) |
| LIKELY VULNERABLE | 1 | In range and ImageMagick in use; lists which of the 3 conditions hold. Untrusted uploads cannot be determined offline |
| UNKNOWN | 2 | Missing Gemfile.lock or application.rb, pre-release, unknown processor, bad CSV |
| NOT EXPOSED | 3 | `:vips` on activestorage >= 7.0, no mini_magick gem, no activestorage, or policy blocks the coders. Upgrade anyway |
| error | 5 | Usage or unreadable input |

Rails < 8.0 is flagged `EOL`; 8.0 gets a note that security support ends 2026-11-07. CSV exit is the worst row (1 > 2 > 3 > 0).
No network, no writes: proven in `run_all.sh` by bwrap with `--unshare-net` and a read-only root, plus a before and after sha256 of the pack.

## Tests

`bash tests/run_all.sh` runs shellcheck, PSScriptAnalyzer (all severities), 73 bash tests, 51 Pester tests,
a fixture check that no MSL operation elements exist, 10 bwrap sandbox runs, `sigma check`, and Kusto
conversion with five pipelines.

## Sigma results

`sigma check`: 0 errors, 0 condition errors, 0 issues. All 5 rules convert with `kusto` under
no pipeline, `microsoft_xdr`, `microsoft_365_defender`, `sentinel_asim` and `azure_monitor`. No pipeline fails
to convert, but two produce semantically wrong output for these Linux rules:

- `azure_monitor`: maps every rule to `SecurityEvent` (Windows Security log). Unusable for Linux.
- `sentinel_asim`: file rules map `Image` to `TargetFilePath` instead of the acting process, so they will not match. Process rules (`imProcessCreate`) look correct.
- `microsoft_xdr` / `microsoft_365_defender`: correct tables; `file_access` maps to `DeviceFileEvents`, which on Linux rarely records reads.

Prefer the hand-written KQL in `kql/`.

## Mitigation

1. Upgrade `activestorage` (and Rails) to 8.0.5.2 or 8.1.4.1, with mini_magick >= 5.1. Prefer 8.1 (8.0 security support ends 2026-11-07). Rails < 8.0: upgrade, there is no fix.
2. Interim: copy the shipped `policy.xml` rules into the system ImageMagick policy (for example `/etc/ImageMagick-6/policy.xml` or `/etc/ImageMagick-7/policy.xml`). Minimum per the advisory:
   ```xml
   <policy domain="coder" rights="none" pattern="MSL"/>
   <policy domain="delegate" rights="none" pattern="gs"/>
   <policy domain="coder" rights="none" pattern="{PS,PS2,PS3,EPS,EPS2,EPS3,EPI,EPSF,EPSI,EPT,EPT2,EPT3,PDF,AI,EPDF,PDFA,POCKETMOD,XPS,PCL}"/>
   ```
3. Or switch to `config.active_storage.variant_processor = :vips` (activestorage >= 7.0 only; 6.x still analyzes with MiniMagick). This does not fix GHSA-v924-7rp8-4mxh.
4. If exposed and using the Disk service: rotate `secret_key_base`, the master key, everything in `config/credentials.yml.enc`, storage and database credentials and third-party tokens. Do not keep exposed secrets as fallbacks.

## Sources (primary only)

- https://github.com/rails/rails/security/advisories/GHSA-f3w6-w445-fwh9
- https://github.com/rails/rails/security/advisories/GHSA-v924-7rp8-4mxh
- https://github.com/rails/rails/commit/ea8c40121ce53a3d0255cd4eb6d13bfe3bc01776 (and `activestorage/lib/active_storage/image_magick/policy.xml` at that commit)
- https://github.com/rails/rails/commit/468aa9a93fc98f5476b6821770f4ccfcb7f7d328
- https://github.com/rails/rails/blob/v8.0.5.2/railties/lib/rails/application/configuration.rb
- https://github.com/rails/rails/blob/v8.0.5.2/activestorage/lib/active_storage/engine.rb
- https://github.com/rails/rails/blob/v6.1.7.10/activestorage/lib/active_storage/analyzer/image_analyzer.rb
- https://rubyonrails.org/2026/11/4/Rails-8.0.5.2-and-8.1.4.1-have-been-released
- https://rubyonrails.org/maintenance
- https://rubygems.org/gems/activestorage/versions
- https://github.com/advisories?query=GHSA-f3w6-w445-fwh9 and https://osv.dev/vulnerability/GHSA-f3w6-w445-fwh9 (not yet indexed at build time)
- https://nvd.nist.gov/ (no record), https://www.cve.org/ (no CVE)
- https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (catalog 2026.11.03)
- https://imagemagick.org/security-policy/
