CVE-2022-39355 (published 2022-10-26T20:15Z)

Discourse Patreon enables syncronization between Discourse Groups and Patreon rewards. On sites with Patreon login enabled, an improper authentication vulnerability could be used to take control of a victim's forum account. This vulnerability is patched in commit number 846d012151514b35ce42a1636c7d70f6dcee879e of the discourse-patreon plugin. Out of an abundance of caution, any Discourse accounts which have logged in with an unverified-email Patreon account will be logged out and asked to verify their email address on their next login. As a workaround, disable the patreon integration and log out all users with associated Patreon accounts.

CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-287
cwe_name: Improper Authentication
description: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
extended_description: 
            
			
         
         

Fix information:
repo_url: https://github.com/discourse/discourse-patreon
commit_details: repo_url: https://github.com/discourse/discourse-patreon
author: David Taylor
author_date: 2022-10-26 19:55:43+01:00
msg: SECURITY: Check for email verification status during login (#92)

This commit also includes a migration which will deactivate any users with existing connections to unverified-email Patreon accounts. They will be asked to verify their email on their next login.
repository: repo_url: https://github.com/discourse/discourse-patreon
repo_name: discourse/discourse-patreon
description: Enable syncronization between Discourse Groups and Patreon rewards
repo_language: Ruby
owner: discourse
file_changes: filename: 20221025153038_deactivate_unverified_email_patreon_accounts.rb
change_type: ModificationType.ADD
diff: @@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class DeactivateUnverifiedEmailPatreonAccounts < ActiveRecord::Migration[6.1]
+  def up
+    execute <<~SQL
+      UPDATE users SET active = false
+      WHERE users.id IN (
+        SELECT user_id FROM user_associated_accounts
+        WHERE provider_name = 'patreon'
+        AND extra -> 'raw_info' -> 'data' -> 'attributes' ->> 'is_email_verified' = 'false'
+      )
+    SQL
+  end
+
+  def down
+    # noop
+  end
+end

programming_language: Ruby
filename: 20221026043851_delete_unverified_patreon_user_info.rb
change_type: ModificationType.ADD
diff: @@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+class DeleteUnverifiedPatreonUserInfo < ActiveRecord::Migration[6.1]
+  def up
+    execute <<~SQL
+      DELETE FROM user_auth_tokens
+      WHERE user_id IN (
+        SELECT user_id
+        FROM user_associated_accounts
+        WHERE provider_name = 'patreon'
+        AND COALESCE(JSON_EXTRACT_PATH(extra::json, 'raw_info', 'data', 'attributes', 'is_email_verified')::text, 'false') <> 'true'
+      )
+    SQL
+
+    execute <<~SQL
+      UPDATE user_api_keys
+      SET revoked_at = NOW()
+      WHERE user_id IN (
+        SELECT user_id
+        FROM user_associated_accounts
+        WHERE provider_name = 'patreon'
+        AND COALESCE(JSON_EXTRACT_PATH(extra::json, 'raw_info', 'data', 'attributes', 'is_email_verified')::text, 'false') <> 'true'
+      )
+    SQL
+
+    execute <<~SQL
+      UPDATE api_keys
+      SET revoked_at = NOW()
+      WHERE created_by_id IN (
+        SELECT user_id
+        FROM user_associated_accounts
+        WHERE provider_name = 'patreon'
+        AND COALESCE(JSON_EXTRACT_PATH(extra::json, 'raw_info', 'data', 'attributes', 'is_email_verified')::text, 'false') <> 'true'
+      )
+    SQL
+
+    execute <<~SQL
+      DELETE FROM user_associated_accounts
+      WHERE provider_name = 'patreon'
+      AND COALESCE(JSON_EXTRACT_PATH(extra::json, 'raw_info', 'data', 'attributes', 'is_email_verified')::text, 'false') <> 'true'
+    SQL
+  end
+
+  def down
+    # noop
+  end
+end

programming_language: SQL
filename: plugin.rb
change_type: ModificationType.MODIFY
diff: @@ -178,6 +178,7 @@ class ::OmniAuth::Strategies::Patreon < ::OmniAuth::Strategies::OAuth2
   info do
     {
       email: raw_info['data']['attributes']['email'],
+      email_verified: raw_info['data']['attributes']['is_email_verified'],
       name: raw_info['data']['attributes']['full_name']
     }
   end
@@ -234,6 +235,10 @@ class Auth::PatreonAuthenticator < Auth::ManagedAuthenticator
   def enabled?
     SiteSetting.patreon_login_enabled
   end
+
+  def primary_email_verified?(auth_token)
+    auth_token[:info][:email_verified]
+  end
 end
 
 auth_provider authenticator: Auth::PatreonAuthenticator.new

programming_language: Ruby