[XSS, CVE] CVE-2025-68116: Bypassing Security Headers for Critical Stored XSS in FileRise

This post details the technical investigation and disclosure of CVE-2025-68116, a Stored Cross-Site Scripting (XSS) vulnerability in the FileRise application. This vulnerability allowed an attacker to execute arbitrary JavaScript in a victim's browser, including logged-in administrators, by exploiting a flaw in how the application served browser-renderable file uploads, specifically SVG files, via public share links.



The issue was particularly notable as it represented a bypass of a prior security mitigation, highlighting the complexity of securely handling user-uploaded content. The vulnerability was ultimately resolved in FileRise v2.7.1.


Vulnerability Summary

The core of the vulnerability lay in the application's failure to consistently apply security headers, which are designed to prevent a browser from rendering a file inline.

CVE ID: CVE-2025-68116

Vulnerability: Stored Cross-Site Scripting (XSS) via Browser-Renderable Uploads (SVG / HTML)

Affected Software: FileRise (versions prior to 2.7.1)

Patched Version: 2.7.1

Reporter's CVSS: 9.6 (Critical)

Official CVSS: 8.9 (High)

Tracking Advisory: GHSA-35pp-ggh6-c59c


The CVSS Scoring Rationale

A key point of discussion during the disclosure was the CVSS score. The official advisory assigned a score of 8.9 (High), using a Privileges Required (PR) metric of Low (PR:L).

However, the reporter argued for a Critical 9.6 score, based on a PR of None (PR:N). This difference stems from the interpretation of the CVSS v3.1 standard:

"CVSS v3.1 defines Privileges Required (PR) as the privileges an attacker must possess at the time the vulnerability is exploited, not the privileges required to place or prepare the vulnerable condition."

Since the exploit payload is delivered when a victim accesses a generated public share link—which requires no authentication or privileges—the reporter's assessment of PR:N more accurately reflects the real-world exploitation scenario. An attacker only needs an account to plant the malicious file, but the exploitation is unauthenticated and "fire-and-forget.


The Incomplete Fix and the Bypass



The history of this vulnerability begins with a prior advisory, GHSA-qrcv-vjvf-fr29, which attempted to address Stored XSS via SVG uploads. That mitigation focused on blocking inline rendering within the FileRise web UI's file preview pane.

Crucially, this initial fix did not address how SVG files were served by backend endpoints responsible for sharing and downloading, such as /api/file/download.php and /api/file/share.php.

CVE-2025-68116 documents the bypass: an attacker could upload a crafted SVG and then use the public share link (/api/file/share.php?token=...) to deliver the payload to any victim, completely bypassing the UI-based mitigation.


Proving Real-World Impact



To move beyond a simple alert() Proof-of-Concept (PoC), the reporter demonstrated a high-impact scenario by having the XSS payload interact with the application's internal APIs.

A test payload was used to fetch an internal API endpoint:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg">

  <script type="text/javascript">

    fetch('/api/upload/upload.php')

      .then(response => response.text())

      .then(data => alert('API Response: ' + data));

  </script>

</svg>

When a logged-in administrator viewed the share link, the script executed and made an authenticated API request in the context of the administrator's session. This proved:


1. Confidentiality: High (C:H): Sensitive information (like CSRF token state) could be exposed to the attacker.

2. Integrity: High (I:H): The interaction was observed to invalidate the administrator's existing CSRF token, causing a practical denial-of-service against their administrative functions.


Root Cause: A Control-Flow Catastrophe



The reason the security headers failed to apply consistently was not a missing line of code, but a fundamental flaw in the control-flow and output ordering within the core file-sharing function, shareFile().

The hardening logic, which was supposed to set headers like X-Content-Type-Options: nosniff and Content-Disposition: attachment, was located late in the function. Two main issues prevented this logic from ever being reached:


1. Early Exits

The shareFile() controller contained multiple conditional blocks that would emit a response header (often Content-Type: application/json or text/html) and then immediately call exit; before the security hardening code was reached.

For example, in the password-protected share flow, if a password was required but not provided, the function would send a Content-Type: text/html header for the password prompt and exit, allowing the browser to render the malicious SVG as HTML if it was served via that path.

An analysis of the controller code revealed numerous early exit points:


// Abridged output from an awk scan on src/controllers/FileController.php

1649 | header('Content-Type: application/json; charset=utf-8');

1651 | exit;

...

1670 | header("Content-Type: text/html; charset=utf-8");

...

1693 | exit;


Because the security headers began at line ~1743, any request hitting an earlier exit; point would bypass the critical security logic.


2. "Headers Already Sent" Errors

In other cases, the hardening logic was prevented from running by PHP's output buffering rules. The reporter captured raw output showing that PHP warnings and notices (e.g., about deprecated constants) were being emitted before the header calls.

This output caused a fatal error when the script attempted to set the security headers:


// Abridged raw output from a curl request

Deprecated: Constant FILTER_SANITIZE_STRING is deprecated in

/data/data/com.termux/files/home/FileRise/src/controllers/FileController.php

on line 1644

...

Warning: Cannot modify header information - headers already sent by

(output started at /data/data/com.termux/files/home/FileRise/src/controllers/FileController.php:1644)

in /data/data/com.termux/files/home/FileRise/src/controllers/FileController.php

on line 1743


The practical result was that the share links returned a default Content-Type: text/html in many scenarios, allowing the browser to render the SVG inline and execute the embedded scripts.


Disclosure Timeline and Final Resolution

The disclosure process involved several iterations as the maintainer attempted to fix the complex control-flow issue:

v2.6.0 - Partial Fix: Mitigation applied to the download endpoint; share endpoint remained vulnerable.

v2.6.2 - Failed Fix: Further attempts; share endpoint still exploitable.

v2.7.0 - Failed Fix: Claimed hardening for share endpoint; still exploitable in testing.

v2.7.1 - Final Fix: Verified to resolve the issue by addressing control-flow and output ordering.

The final fix in v2.7.1 addressed the root cause by ensuring the security header logic was executed before any output and correcting the early exit; points.


Verification on v2.7.1 confirmed the correct security headers were being applied:


$ curl -svI "..." | grep -iE "content-type|content-disposition"

< Content-Type: application/octet-stream

< Content-Disposition: attachment; filename="xss-image.svg"; filename*=UTF-8''xss-image.svg


By forcing the file to download (Content-Disposition: attachment) and serving it with a safe MIME type (application/octet-stream), the browser is prevented from rendering the SVG inline, and the XSS payload is neutralized.


Conclusion

CVE-2025-68116 serves as a powerful reminder that security is often about more than just adding a single line of code. Complex control-flow logic, especially in older codebases, can create subtle bypasses that undermine otherwise sound security mitigations. Developers are strongly advised to upgrade their FileRise installations to v2.7.1 or later immediately.


This technical write-up is based on the original disclosure (https://github.com/x0root/CVE-2025-68116)

Comments

Popular posts from this blog

[XSS] Breaking ‘safe’ embeds via frame-src bypass

About Me

[BYPASS] IKEv2 Fortinet Misconfiguration