CVE-2020-26236 (published 2020-11-20T18:15Z)

In ScratchVerifier before commit a603769, an attacker can hijack the verification process to log into someone else's account on any site that uses ScratchVerifier for logins. A possible exploitation would follow these steps: 1. User starts login process. 2. Attacker attempts login for user, and is given the same verification code. 3. User comments code as part of their normal login. 4. Before user can, attacker completes the login process now that the code is commented. 5. User gets a failed login and attacker now has control of the account. Since commit a603769 starting a login twice will generate different verification codes, causing both user and attacker login to fail. For clients that rely on a clone of ScratchVerifier not hosted by the developers, their users may attempt to finish the login process as soon as possible after commenting the code. There is no reliable way for the attacker to know before the user can finish the process that the user has commented the code, so this vulnerability only really affects those who comment the code and then take several seconds before finishing the login.

Severity: MEDIUM
CVSS v2 base score: 5.1 (AV:N/AC:H/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:H/PR:N/UI:R/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/ScratchVerifier/ScratchVerifier
commit_details: repo_url: https://github.com/ScratchVerifier/ScratchVerifier
author: Kenny2github
author_date: 2020-11-20 03:07:16+08:00
msg: Change behavior of PUT /verify/{}

* Instead of returning the same code, which someone else could hijack,
  generate a new code on repeated PUT to preserve integrity.
* Update docs to reflect this new behavior
repository: repo_url: https://github.com/ScratchVerifier/ScratchVerifier
repo_name: ScratchVerifier/ScratchVerifier
description: Verify Scratch accounts as genuine, for use in authorization or identification.
repo_language: Python
owner: ScratchVerifier
file_changes: filename: db.py
change_type: ModificationType.MODIFY
diff: @@ -162,11 +162,6 @@ WHERE username=?', (username,))
             await self.db.execute('SELECT code FROM scratchverifier_usage WHERE \
 client_id=? AND username=?', (client_id, username))
             row = await self.db.fetchone()
-        if row is not None:
-            await self.db.execute('UPDATE scratchverifier_usage SET expiry=? \
-WHERE client_id=? AND username=? AND code=?', (int(time.time()) + VERIFY_EXPIRY,
-                                               client_id, username, row[0]))
-            return row[0]
         code = sha256(
             str(client_id).encode()
             + str(time.time()).encode()
@@ -174,6 +169,12 @@ WHERE client_id=? AND username=? AND code=?', (int(time.time()) + VERIFY_EXPIRY,
             + token_bytes()
         # 0->A, 1->B, etc, to avoid Scratch's phone number censor
         ).hexdigest().translate({ord('0') + i: ord('A') + i for i in range(10)})
+        if row is not None:
+            await self.db.execute(
+                'UPDATE scratchverifier_usage SET expiry=?, code=? \
+WHERE client_id=? AND username=?', (int(time.time()) + VERIFY_EXPIRY,
+                                    code, client_id, username))
+            return code
         await self.db.execute('INSERT INTO scratchverifier_usage (client_id, \
 code, username, expiry) VALUES (?, ?, ?, ?)', (client_id, code, username,
                                int(time.time() + VERIFY_EXPIRY)))

programming_language: Python
filename: docs.json
change_type: ModificationType.MODIFY
diff: @@ -59,7 +59,7 @@
     {
       "type": "endpoint",
       "name": "Start/Renew Verification",
-      "desc": "Request a new verification code for a user. Only one code per user per client - if this endpoint is used again before the <a href=\"#finish-verification-endpoint\">Finish Verification</a> endpoint is used, this will instead renew the 30-minute expiry on the code and return the original code.",
+      "desc": "Request a new verification code for a user. Only one code per user per client - if this endpoint is used again before the <a href=\"#finish-verification-endpoint\">Finish Verification</a> endpoint is used, this will generate a new code and reset the 30-minute expiry, returning the new code instead.",
       "method": "PUT",
       "path": "/verify/{username}",
       "params": {

programming_language: JavaScript
filename: reference.html
change_type: ModificationType.MODIFY
diff: @@ -21,7 +21,7 @@
 <h2 id="api-endpoints">API Endpoints</h2>
 <p>The simplicity of this API is such that there are only three total endpoints for its ultimate purpose.</p>
 <h3 id="start/renew-verification-endpoint">Start/Renew Verification Endpoint</h3>
-<p>Request a new verification code for a user. Only one code per user per client - if this endpoint is used again before the <a href="#finish-verification-endpoint">Finish Verification</a> endpoint is used, this will instead renew the 30-minute expiry on the code and return the original code.</p>
+<p>Request a new verification code for a user. Only one code per user per client - if this endpoint is used again before the <a href="#finish-verification-endpoint">Finish Verification</a> endpoint is used, this will generate a new code and reset the 30-minute expiry, returning the new code instead.</p>
 <div class="endpoint">
 <div class="method-path auth-needed" onclick="showOrHide(this)"><span class="method">PUT</span> <code>/verify/<span class="param">{username}</span></code> <a href="#authorization"><img src="https://image.flaticon.com/icons/png/512/61/61457.png" title="Authorization necessary" /></a></div>
 <div style="display: none">

programming_language: HTML