IPBUF安全漏洞报告
English
CVE-2025-61598 CVSS 5.3 中危

Discourse Cache-Control响应头缺失导致缓存投毒漏洞(CVE-2025-61598)

披露日期: 2025-10-28

漏洞信息

漏洞编号
CVE-2025-61598
漏洞类型
缓存投毒
CVSS评分
5.3 中危
攻击向量
网络 (AV:N)
认证要求
无需认证 (PR:N)
用户交互
无需交互 (UI:N)
影响产品
Discourse

相关标签

暂无标签

漏洞概述

Discourse是一个开源讨论平台,被全球众多社区和论坛广泛使用。该平台在3.6.2之前版本及3.6.0.beta2之前版本中存在一个缓存控制相关的安全漏洞。具体表现为:Discourse的错误响应中缺少默认的Cache-Control响应头(值为'no-store, no-cache'),这导致Web代理服务器和CDN可能会意外缓存这些错误响应页面。攻击者可以利用这一缺陷,通过精心构造的恶意请求触发特定的错误响应,并诱导代理服务器缓存这些响应。当其他合法用户访问相同路径时,会从代理缓存中接收到被污染的内容,从而可能导致缓存投毒攻击。这种攻击可以造成会话劫持、敏感信息泄露、恶意内容注入等严重安全问题。由于Discourse是一个社区平台,用户量通常较大,因此该漏洞的影响范围可能相当广泛。

技术细节

该漏洞属于Web缓存投毒(Cache Poisoning)类型,源于Discourse在错误响应处理中未正确设置缓存控制头。正常情况下,Web应用应通过Cache-Control: no-store, no-cache响应头来指示代理服务器不要缓存敏感内容,特别是错误响应。Discourse在处理错误响应时遗漏了这一关键的安全头,导致代理服务器(如Varnish、Nginx、CDN等)可能将这些错误页面缓存起来。攻击者可以构造特定的请求参数或路径,触发Discourse返回错误响应(如404、500等错误页面)。由于缺少no-store指令,代理服务器会将这些错误响应缓存并保留一段时间。此后,任何访问相同URL的合法用户都会从缓存中获取被污染的错误页面。攻击者可以在缓存的错误页面中注入恶意JavaScript代码、钓鱼内容或重定向脚本,从而对后续访问该页面的用户发动攻击。这种攻击的隐蔽性较高,因为用户访问的是原本可信的域名,但实际上收到的是被篡改的缓存内容。

攻击链分析

STEP 1
步骤1:侦察和信息收集
攻击者首先识别目标Discourse实例版本,确认其版本在3.6.2之前或3.6.0.beta2之前。通过访问错误页面或特定端点,观察响应头中是否缺少Cache-Control: no-store, no-cache指令。
STEP 2
步骤2:构造恶意请求
攻击者构造特定的URL或请求参数,目标是触发Discourse返回错误响应(如404 Not Found、500 Internal Server Error等)。这些请求通常包含特殊字符、非法参数或指向不存在的资源。
STEP 3
步骤3:利用代理缓存机制
由于Discourse错误响应缺少no-store指令,攻击者向代理服务器(如Varnish、Nginx、CDN)发送请求。代理服务器在未检测到no-store头的情况下,会将错误响应缓存到其存储中。
STEP 4
步骤4:缓存投毒
攻击者通过HTTP响应走私(HTTP Response Smuggling)或直接污染代理缓存的方式,在缓存的错误页面中注入恶意内容,如XSS payload、钓鱼表单或重定向脚本。
STEP 5
步骤5:等待目标用户访问
当合法用户访问相同的URL路径时,代理服务器会从缓存中返回被污染的错误页面。用户在不知情的情况下会与恶意内容交互,导致会话被劫持、凭据被盗或其他安全后果。
STEP 6
步骤6:实施攻击
攻击者利用注入的恶意内容实施进一步攻击,例如窃取用户会话Cookie、诱导用户输入敏感信息、或将用户重定向到钓鱼网站。这种攻击对用户来说是隐蔽的,因为他们访问的是原本可信的域名。

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
# CVE-2025-61598 PoC - Discourse Cache Poisoning # This PoC demonstrates how missing Cache-Control headers can lead to cache poisoning import requests import time TARGET_URL = "https://vulnerable-discourse-instance.com" EVIL_PAYLOAD = "<script>alert('Cache Poisoned - XSS')</script>" def test_cache_poisoning(): """ Test for missing Cache-Control headers in error responses """ # Step 1: Trigger an error response print("[+] Step 1: Triggering error response...") error_urls = [ f"{TARGET_URL}/nonexistent-page-12345", f"{TARGET_URL}/?error_trigger=1", f"{TARGET_URL}/invalid/path/to/trigger/error" ] for url in error_urls: response = requests.get(url) cache_control = response.headers.get('Cache-Control', 'NOT SET') print(f" URL: {url}") print(f" Status: {response.status_code}") print(f" Cache-Control: {cache_control}") # Check if Cache-Control is missing or allows caching if 'no-store' not in cache_control.lower() or 'not set' in cache_control.lower(): print(f" [!] VULNERABLE: Cache-Control header missing or not set to 'no-store'") # Step 2: Simulate cache poisoning by injecting malicious content print("\n[+] Step 2: Simulating cache poisoning...") poisoned_url = f"{TARGET_URL}/error-page" # In real attack, attacker would poison the cache with malicious content # The proxy would cache this response without no-store directive # Step 3: Verify if subsequent requests get cached content print("[+] Step 3: Checking if response can be cached...") response1 = requests.get(poisoned_url) response2 = requests.get(poisoned_url) if response1.text == response2.text: print(" [i] Response is consistent - may be cached") print("\n[!] Note: Full exploitation requires proxy server interaction") print("[!] Attacker needs to poison the proxy cache with malicious content") if __name__ == "__main__": test_cache_poisoning()

影响范围

Discourse < 3.6.2
Discourse 3.6.0.beta2 之前的所有beta版本

防御指南

临时缓解措施
如果无法立即升级Discourse版本,建议在反向代理层面(如Nginx、Apache、Varnish)配置强制性的缓存控制策略。对于所有错误响应(4xx和5xx状态码),强制设置Cache-Control: no-store, no-cache, must-revalidate和Pragma: no-cache头。同时,配置代理服务器忽略后端服务器可能设置的任何缓存指令,确保错误页面不会被任何缓存层存储。此外,考虑使用WAF(Web应用防火墙)规则来检测和阻止可能的缓存投毒尝试。

参考链接

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