CVE-2023-47634
Executive Summary
CVE-2023-47634 is a race condition vulnerability in the Decidim participatory democracy framework, affecting versions 0.10.0 through 0.26.8, 0.27.0 through 0.27.4, and 0.28.0-rc1. The flaw allows an authenticated user to endorse a resource (e.g., a proposal) more than once by sending parallel endorsement requests. This violates the intended single-endorsement-per-user constraint, potentially skewing community voting or feedback mechanisms. The vulnerability has a low CVSS score of 3.1 due to the requirement for authenticated access and the limited integrity impact. No public exploit code or proof-of-concept is currently available, and the EPSS score is 0.0%, indicating a very low likelihood of exploitation in the wild. Patches are available in versions 0.26.9, 0.27.5, and 0.28.0. As a workaround, administrators can disable the Endorsement feature in affected components.
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.
No signup required
Supply Chain & Package Security
Decidim is a Ruby on Rails-based open-source framework distributed as a Ruby gem. The vulnerability exists in the core endorsement logic, not in a third-party dependency. However, organizations using Decidim should verify their gem versions and update to patched releases. The supply chain risk is low, as the fix is available in official releases.
CVSS Breakdown & Threat Level
- CVSS Score: 3.1 (Low)
- Vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N
- Attack Vector: Network (AV:N) – The attack is performed over the network.
- Attack Complexity: High (AC:H) – Exploitation requires precise timing and parallel request handling.
- Privileges Required: None (PR:N) – The attacker must be authenticated to the Decidim instance.
- User Interaction: None (UI:N) – No user interaction is required beyond the attacker’s own actions.
- Scope: Unchanged (S:U) – The vulnerability does not affect other components.
- Confidentiality Impact: None (C:N) – No data is exposed.
- Integrity Impact: Low (I:L) – The attacker can artificially inflate endorsement counts.
- Availability Impact: None (A:N) – No denial of service.
Threat Level: Low. The vulnerability requires authenticated access and precise timing, and the impact is limited to integrity manipulation of endorsement counts.
Deep Dive & Mechanics
The vulnerability is a classic race condition (CWE-362) in the endorsement creation logic. When a user endorses a resource, the application performs a check to ensure the user has not already endorsed that resource. However, this check is not atomic. If multiple endorsement requests are sent in parallel, they may all pass the check before any of them commit the endorsement, allowing the user to endorse the same resource multiple times.
Technical Flow:
- User sends a POST request to
/proposals/:id/endorsements(or similar endpoint). - The controller action calls a service object (e.g.,
Decidim::Endorsable::CreateEndorsement) that checks if the user has already endorsed the resource. - If the check passes, a new endorsement record is created in the database.
- Due to the race condition, if multiple requests arrive simultaneously, multiple checks may pass before any record is committed, resulting in multiple endorsements.
The fix likely involves using database-level unique constraints or atomic operations (e.g., find_or_create_by! with a unique index) to prevent duplicate endorsements.
Vulnerable vs. Patched Code
Vulnerable Code (Conceptual):
# app/services/decidim/endorsable/create_endorsement.rb
def call
return broadcast(:invalid) if already_endorsed?
create_endorsement!
broadcast(:ok)
end
private
def already_endorsed?
Endorsement.exists?(user: current_user, resource: resource)
end
def create_endorsement!
Endorsement.create!(user: current_user, resource: resource)
end
Patched Code (Conceptual):
# app/services/decidim/endorsable/create_endorsement.rb
def call
create_endorsement!
broadcast(:ok)
rescue ActiveRecord::RecordNotUnique
broadcast(:invalid)
end
private
def create_endorsement!
Endorsement.create!(user: current_user, resource: resource)
end
The patched version relies on a database-level unique constraint on (user_id, resource_id) to prevent duplicate endorsements, making the operation atomic.
Exploitation & Lab Setup
Red Team
Prerequisites:
- Access to a Decidim instance (version 0.10.0 to 0.26.8, 0.27.0 to 0.27.4, or 0.28.0-rc1).
- Valid user credentials with permission to endorse resources.
Exploitation Steps:
- Identify an endpoint that handles endorsements (e.g.,
/proposals/:id/endorsements). - Capture a valid POST request to that endpoint using a proxy (e.g., Burp Suite).
- Use a tool like
curlwith parallel execution or a custom script to send multiple identical requests simultaneously.
Example using curl and xargs:
# Send 10 parallel endorsement requests
seq 1 10 | xargs -P 10 -I {} curl -X POST -H "Cookie: session=..." \
-d "authenticity_token=..." \
"https://target.decidim.org/proposals/1/endorsements"
Expected Outcome: The resource’s endorsement count increases by more than 1, indicating successful exploitation.
Blue Team
Detection:
- Monitor for multiple identical POST requests to endorsement endpoints within a short time window (e.g., < 1 second).
- Look for anomalies in endorsement counts per user per resource.
Hunting:
- Query application logs for repeated endorsement attempts from the same user on the same resource.
- Use database queries to find users with multiple endorsements on the same resource.
Example Database Query:
SELECT user_id, resource_id, COUNT(*) as endorsement_count
FROM decidim_endorsements
GROUP BY user_id, resource_id
HAVING COUNT(*) > 1;
Indicators of Compromise (IOCs)
- Network: Multiple identical POST requests to
/proposals/*/endorsementsor similar endpoints within a short timeframe. - Application Logs: Repeated
Endorsement::Createactions for the same user and resource. - Database: Duplicate rows in the
decidim_endorsementstable with the sameuser_idandresource_id.
Detection Engineering
YARA Rule
rule CVE_2023_47634_RaceCondition {
meta:
description = "Detects potential race condition exploitation in Decidim endorsement endpoints"
author = "Valters IT"
date = "2024-01-01"
reference = "CVE-2023-47634"
strings:
$endpoint = "/endorsements" nocase
$method = "POST" nocase
$param = "authenticity_token" nocase
condition:
# of ($endpoint) > 0 and # of ($method) > 0 and # of ($param) > 0
}
Sigma Rule
title: Multiple Endorsement Requests in Short Timeframe
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects potential race condition exploitation in Decidim endorsement endpoints
author: Valters IT
date: 2024-01-01
references:
- https://nvd.nist.gov/vuln/detail/CVE-2023-47634
logsource:
category: application
product: decidim
detection:
selection:
http.method: POST
http.url|contains: "/endorsements"
timeframe: 1s
condition: selection | count() by user.id, resource.id > 1
falsepositives:
- Legitimate rapid endorsement by multiple users
level: medium
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"CVE-2023-47634 - Decidim Endorsement Race Condition"; flow:to_server,established; content:"POST"; http_method; content:"/endorsements"; http_uri; pcre:"/authenticity_token=/"; threshold:type both, track by_src, count 5, seconds 1; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate:
- Upgrade to Decidim version 0.26.9, 0.27.5, or 0.28.0 (or later).
- If upgrade is not possible, disable the Endorsement feature in affected components via the admin panel.
Long-term:
- Implement database-level unique constraints on endorsement tables to prevent duplicate entries.
- Use atomic database operations (e.g.,
find_or_create_by!with unique indexes) to avoid race conditions. - Conduct regular security audits of custom code for similar race condition vulnerabilities.
Workaround:
- In the admin panel, navigate to the component settings and disable the "Endorsements" option.
Valters IT
Securing Digital Democracy