Created
December 21, 2019 23:09
-
-
Save mschmitt/c214bfc560881ac3d68c5b11298ba318 to your computer and use it in GitHub Desktop.
steam_login.sh - proof of concept for automatically logging in to Steam
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Wrapper for verbose exiting | |
function errexit(){ | |
echo "Error: $1" | |
exit 1 | |
} | |
# Check for availability of external prerequisites | |
[[ -x "$(command -v curl)" ]] || errexit "curl is required but not installed" | |
[[ -x "$(command -v jq)" ]] || errexit "jq is required but not installed" | |
[[ -x "$(command -v openssl)" ]] || errexit "openssl is required but not installed" | |
# Curl cookie storage | |
COOKIEJAR=~/.cookiejar.steam | |
CURL="curl --progress-bar --cookie-jar $COOKIEJAR --cookie $COOKIEJAR" | |
# Test whether we're already logged in. | |
# Permalink to own profile found at: | |
# https://www.reddit.com/r/Steam/comments/30kvjt/link_that_brings_you_to_your_own_profile/ | |
LOCATION=$($CURL -o/dev/null -w '%{redirect_url}' https://steamcommunity.com/my/profile) | |
# If we get redirected to a login page, we aren't already logged in. | |
if [[ ! "$LOCATION" =~ steamcommunity.com/login/home/ ]] | |
then | |
printf "Already logged in. Profile URL is: %s\n" "$LOCATION" | |
exit 0 | |
fi | |
# Clean up all the temp files we're going to create: | |
function cleanup(){ | |
rm -f "$CURLTMP" | |
rm -f "$ASN1TMP" | |
rm -f "$DERTMP" | |
rm -f "$PEMTMP" | |
rm -f "$RSATMP" | |
} | |
trap cleanup INT QUIT TERM EXIT | |
CURLTMP="$(mktemp)" | |
ASN1TMP="$(mktemp)" | |
DERTMP="$(mktemp)" | |
PEMTMP="$(mktemp)" | |
RSATMP="$(mktemp)" | |
# User and password can be supplied as environment variables | |
if [[ ! -v STEAMUSER ]] | |
then | |
read -p "Steam Username: " STEAMUSER | |
fi | |
if [[ ! -v STEAMPASS ]] | |
then | |
read -p "Steam Password: " STEAMPASS | |
fi | |
# Get the RSA key that will be used to encrypt the password | |
$CURL 'https://steamcommunity.com/login/getrsakey/' \ | |
--form "username=$STEAMUSER" \ | |
> "$CURLTMP" || errexit 'curl_rsakey' | |
SUCCESS=$(jq --raw-output '.success' < "$CURLTMP") | |
if [[ ! "$SUCCESS" == 'true' ]] | |
then | |
errexit 'get rsakey' | |
fi | |
# Encryption requires modulus and exponent, | |
# login requires the timestamp that was received. | |
MOD=$(jq --raw-output '.publickey_mod' < "$CURLTMP") | |
EXP=$(jq --raw-output '.publickey_exp' < "$CURLTMP") | |
TIME=$(jq --raw-output '.timestamp' < "$CURLTMP") | |
# Generate an RSA public key from modulus and exponent | |
# by providing an ASN1 description file. | |
# https://stackoverflow.com/a/36448243/263310 | |
cat >>"$ASN1TMP" <<End | |
# Start with a SEQUENCE | |
asn1=SEQUENCE:pubkeyinfo | |
# pubkeyinfo contains an algorithm identifier and the public key wrapped | |
# in a BIT STRING | |
[pubkeyinfo] | |
algorithm=SEQUENCE:rsa_alg | |
pubkey=BITWRAP,SEQUENCE:rsapubkey | |
# algorithm ID for RSA is just an OID and a NULL | |
[rsa_alg] | |
algorithm=OID:rsaEncryption | |
parameter=NULL | |
# Actual public key: modulus and exponent | |
[rsapubkey] | |
n=INTEGER:0x$MOD | |
e=INTEGER:0x$EXP | |
End | |
# Generate an actual public key from ASN1, DER only. | |
openssl asn1parse -genconf "$ASN1TMP" -out "$DERTMP" -noout || errexit 'asnparse' | |
# Convert to PEM because this is how I roll | |
openssl rsa -in "$DERTMP" -inform der -pubin -out "$PEMTMP" 2>/dev/null || errexit 'rsa' | |
# Encrypt the password | |
echo -n "$STEAMPASS" | openssl rsautl -encrypt -pkcs -inkey "$PEMTMP" -pubin > "$RSATMP" || errexit 'rsautl' | |
# Encode the encrypted password as b64, remove linebreaks | |
CRYPTPW=$(openssl base64 < "$RSATMP" | tr -d '\n') || errexit 'base64' | |
# Log in using user name and password | |
$CURL 'https://steamcommunity.com/login/dologin/' \ | |
--data-urlencode "username=$STEAMUSER" \ | |
--data-urlencode "password=$CRYPTPW" \ | |
--data-urlencode "twofactorcode=" \ | |
--data-urlencode "rsatimestamp=$TIME" \ | |
--data-urlencode "remember_login=true" > "$CURLTMP" || errexit 'curl_pre_2fa' | |
# Steam should now return a request for two factor authentication | |
# We could simply ask the user for 2FA and submit it, but then they | |
# wouldn't get the popup on their mobile phone. | |
# | |
# If Steam asks for a CAPTCHA, you've been recognized | |
# as being a machine and the world has officially ended. | |
CAPTCHA=$(jq --raw-output '.captcha_needed' < "$CURLTMP") | |
REQ2FA=$(jq --raw-output '.requires_twofactor' < "$CURLTMP") | |
if [[ "$CAPTCHA" == 'true' ]] | |
then | |
errexit 'response_steam_wants_captcha' | |
fi | |
if [[ ! "$REQ2FA" == 'true' ]] | |
then | |
errexit 'response_pre_2fa' | |
fi | |
# Ask for 2FA token and resubmit same request as above. | |
read -p "Steam 2-factor token: " STEAM2FATOKEN | |
$CURL 'https://steamcommunity.com/login/dologin/' \ | |
--data-urlencode "username=$STEAMUSER" \ | |
--data-urlencode "password=$CRYPTPW" \ | |
--data-urlencode "twofactorcode=$STEAM2FATOKEN" \ | |
--data-urlencode "rsatimestamp=$TIME" \ | |
--data-urlencode "remember_login=true" > "$CURLTMP" || errexit 'curl_post_2fa' | |
# Final check for completed login. | |
LOGIN=$(jq --raw-output '.login_complete' < "$CURLTMP") | |
if [[ "$LOGIN" == 'true' ]] | |
then | |
echo "Login succeeded." | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment