
A Documentation of CVE-2025-68116
Author: @x0root
Vulnerability: Stored Cross-Site Scripting (XSS) via Browser-Renderable Uploads (SVG / HTML)
Affected Software: FileRise (< 2.7.1)
Patched Version: 2.7.1
Official CVE (requested via GHSA): CVE-2025-68116 (tracking/advisory: GHSA-35pp-ggh6-c59c)
Prior related advisory (original mitigation that was bypassed): GHSA-qrcv-vjvf-fr29
CVSS Assessment:
The reporter’s assessment evaluates Privileges Required (PR) at the point of exploitation, not at the point of vulnerability planting.
Exploitation occurs when a victim accesses a generated public share link, which requires no authentication or privileges (PR:N).
The CNA assessment evaluates PR based on the ability to upload a malicious file. However, 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.
As such, PR:N more accurately reflects real-world exploitation conditions, resulting in a Critical (9.6) severity classification.
Note: GHSA-qrcv-vjvf-fr29 introduced a mitigation that prevented SVGs from rendering inside the FileRise web UI (preview pane). This report documents a bypass of that mitigation—specifically the backend share/download endpoints—which is tracked as GHSA-35pp-ggh6-c59c / CVE-2025-68116.
This document is a complete technical record of CVE-2025-68116: a Stored XSS in FileRise that persisted after an earlier mitigation and was ultimately fixed in v2.7.1. It includes discovery, exploitation proof-of-concepts, repeated failed fixes, a precise root-cause control-flow analysis (with evidence), final verification of the patch, and an analysis of exploitability characteristics relevant to CVSS assessment. All content below is based on reproduced tests, controller inspection, and the public advisory thread.
A prior advisory, GHSA-qrcv-vjvf-fr29, addressed Stored XSS via SVG uploads by blocking inline rendering in the FileRise web UI. That mitigation did not address how SVG files were served by backend endpoints such as:
/api/file/download.php/api/file/share.phpCVE-2025-68116 (tracked as GHSA-35pp-ggh6-c59c) documents a bypass of the GHSA-qrcv-vjvf-fr29 mitigation: an attacker can store a crafted SVG and deliver it to victims via public share links or certain download behaviours, leading to script execution in the FileRise origin.
To validate whether the backend still exposed SVGs in a renderable way, I uploaded a simple PoC SVG:
Accessing the file via:
/api/file/download.php?…/api/file/share.php?token=…resulted in alert() executing. The original GHSA-qrcv-vjvf-fr29 mitigation (UI preview block) was bypassed by direct access to these endpoints.
An alert() is a PoC; I tested for meaningful impact by having the payload interact with internal APIs.
Test payload used:
<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 opened a share link containing this SVG, the script executed and made authenticated API requests. Observed effects included:
{"csrf_expired":true,"csrf_token":"..."})Impact classification demonstrated during testing:
I reported the issue privately. The maintainer released several incremental fixes:
Throughout v2.6.0 → v2.7.0, the share link endpoint continued to serve the SVG in a way that allowed inline rendering and script execution. The root cause analysis below explains why the earlier fixes failed to fully close the vector.
The underlying cause was not a single missing header but control-flow and output ordering inside shareFile() (controller) that prevented the security headers from being applied in many execution paths. Two classes of problems were present:
exit; points that short-circuited the function before security headers were set.I used an awk scan to list header() and exit; occurrences within shareFile() up to the readfile() call:
Command: awk '/function shareFile(/ {flag=1} /readfile(/ {flag=0} flag && /(header|exit;)/ {printf "%4d | %s\n", NR, $0}' src/controllers/FileController.php
Observed output (abridged from my run):
1649 | header('Content-Type: application/json; charset=utf-8'); 1651 | exit; 1657 | header('Content-Type: application/json; charset=utf-8'); 1659 | exit; 1664 | header('Content-Type: application/json; charset=utf-8'); 1666 | exit; 1670 | header("Content-Type: text/html; charset=utf-8"); 1693 | exit; 1699 | header('Content-Type: application/json; charset=utf-8'); 1701 | exit; 1719 | header('Content-Type: application/json; charset=utf-8'); 1721 | exit; 1725 | header('Content-Type: application/json; charset=utf-8'); 1727 | exit;
Security headers (the hardening logic) begin at line ~1743:
1743 | header('X-Content-Type-Options: nosniff'); ... 1770 | header("Content-Disposition: attachment; ...");
Because the function emits headers + exit; earlier in many paths, those requests never reached the hardening code that sets Content-Disposition, nosniff, or the restrictive type.
In the password-protected share flow, the function emitted the password prompt HTML early:
if (!empty($record['password']) && empty($providedPass)) { header("Content-Type: text/html; charset=utf-8"); ... exit; }
This path sends Content-Type: text/html and exits before the SVG hardening logic, causing inline rendering in browsers for password-protected shares where no password was provided.
The vulnerability was not limited to password-protected flows. A non-password share request returned text/html as well in my tests:
Command: curl -svI "http://127.0.0.1:8080/api/file/share.php?token=437d7913884ace4b94fab8ce745a686a" 2>&1 | grep -iE "content-type"
Observed: < Content-Type: text/html; charset=UTF-8
This confirms that even in the general (non-password) case the response was text/html, and the SVG rendered inline.
I captured a raw fetch of the share endpoint which included PHP warnings emitted before header hardening. Snapshot (abridged):