Gaining access to private topics using quoting feature (#312647)

Report Meta

StateResolved (Closed)
Disclosed publiclyMarch 18, 2018 12:27am +0600
Reported ToDiscourse
WeaknessImproper Access Control - Generic
Bounty$256
SeverityHigh (7 ~ 8.9)
VisibilityPublic (Full)

3 Participants

  • 50x50
  • 50x50
  • avatar
avatar
mishre submitted a report to Discourse.
on Mar 18, 2018

Description

Some topics have limited access to certain groups and users, and while there exists a validation for access on this topic, it can be bypassed by abusing a vulnerability in the "onebox" quoting feature. When pasting a link in a reply, if this link happens to be a link to another topic on Discourse a small preview is shown which includes the topic content or the post content where the link is pointing to. Also there are some protections in place to make sure that the user can view the linked content, the said protections can be bypassed by adding a query string parameter to the link containing the value

?source_topic_id={victim-topic-id}

Steps to reproduce

  1. Login as an administrator to Discourse and create a topic which can only be viewed by the staff.
  2. Copy the topic's id from the topic's page. the topic id can be found by browsing the topic and then copying the number in the end of the url (http://localhost:4000/t/{topic-name}/{topic-id})
  3. Login with a non-admin user.
  4. Go to any topic you have access to, and type in the following reply: http://localhost:80/t/blablabla/?source_topic_id=29 please note that the port should 80 or 443 even if the url of your local installation is a different (probably some software bug)
  5. Wait for the preview to load and see that you can see topic's content.

Root cause

The following piece of code determines if the logged-in user is capable of viewing the post/topic :

def can_see_post?(post, source_topic)
    return false if post.nil? || post.hidden || post.trashed? || post.topic.nil?
    Guardian.new.can_see_post?(post) || same_category?(post.topic.category, source_topic)
  end

  def can_see_topic?(topic, source_topic)
    return false if topic.nil? || topic.trashed? || topic.private_message?
    Guardian.new.can_see_topic?(topic) || same_category?(topic.category, source_topic)
  end

as can be seen here: https://github.com/discourse/discourse/blob/master/lib/onebox/engine/discourse_local_onebox.rb#L113

However, the source_topic parameter is controlled directly by user input:

source_topic_id = [@url](/url)[/[&?]source_topic_id=(\d+)/, 1].to_i

as can be seen here: https://github.com/discourse/discourse/blob/master/lib/onebox/engine/discourse_local_onebox.rb#L47 So if we pass in the same topic id as the one we are trying to view, basically the function same_category will always return true, effectively bypassing any protection in place.

Impact

An attacker will be able to access all private topics and posts on Discourse.

avatar
mishre posted a comment.
on Mar 19, 2018

Oups, I have posted an incorrect link in step 4. The link should have been:

http://localhost:80/t/blablabla/{copied-topic-id}?source_topic_id={copied-topic-id}

for example: http://localhost/t/blablabla/29?source_topic_id=29

avatar
glassofbeer CESPPA staff changed the status to Needs more info.
on Mar 19, 2018

@mishre

Thanks for the submission. I was not able to reproduce the issue. If I got your report correct, there's no need to have a link between both accounts...right? Basically, any account should be able to see any topic using this bug, is that correct?

I'm trying to reproduce this on the live demo site with a random topic id, but the preview never opened:

Can you double check you PoC and the permissions/groups for both IDs you've used in your PoC?

Best Regards.

avatar
mishre changed the status to New.
on Mar 19, 2018

Hey,

I have tested this on my local deployment with a topic id that I know exists and it seems to work. Are you sure that a topic with this id exists on the live demo site? (the topic could have been deleted, or it could actually be a private-message which is also represented by a topic object).

And yes, anyone can view any private topic using this bug.

avatar
glassofbeer CESPPA staff changed the status to Needs more info.
on Mar 19, 2018

@mishre

Can you record a video of the exploitation in motion?

avatar
discourse_team posted a comment.
on Mar 19, 2018

The repro steps are off, the URL is incorrect.. try this one:

https://try.discourse.org/t/blah/128?source_topic_id=128

There is a topic 128 on try.discourse.org that you shouldn't have visibility into, and you can repro that way.

avatar
mishre changed the status to New.
on Mar 19, 2018

Hi,

It seems I can view this topic. The title of topic is: "Secret assets that should not be removed". From some reason I cannot seem to be able to embed different images in it, However I do seem to see the text inside. I can also see that there is a single comment in the topic which was written by the user codinghorror and contains an image.

See attached images.

2 attachments:

If you are still not able to reproduce I'll record a video.

discourse_team changed the status to Triaged.
on Mar 19, 2018
avatar
discourse_team posted a comment.
on Mar 19, 2018

Thanks @mishre for the report. We're working on a fix.

avatar
Discourse rewarded mishre with a $256 bounty.
on Mar 19, 2018

OK, thanks for this repro excellent as usual!

discourse_team closed the report and changed the status to Resolved.
on Mar 19, 2018
avatar
mishre requested to disclose this report publicly.
on Mar 19, 2018

That was a pretty interesting bug, mind disclosing?

This report has been publicly disclosed.
on Mar 19, 2018
avatar

Leave a comment