Jump to content
McKay Development

eXPerience

Member
  • Posts

    2
  • Joined

  • Last visited

Everything posted by eXPerience

  1. Thank you @Dr. McKay for the pointers. The solution was the `server_secret` field for the ticket in the CMsgClientAuthList message. I couldnt find out how this value is obtained/generated so I'm settling on patching steam-user locally. The server secret was just the shortened name of the game. Diff: diff --git a/components/appauth.js b/components/appauth.js index 3f6b5b6..1f29a0f 100644 --- a/components/appauth.js +++ b/components/appauth.js @@ -84,7 +84,7 @@ class SteamUserAppAuth extends SteamUserAccount { return AppTicket.parseAppTicket(ticket, allowInvalidSignature); } - createAuthSessionTicket(appid, callback) { + createAuthSessionTicket(appid, server_secret_hex_string, callback) { return StdLib.Promises.callbackPromise(['sessionTicket'], callback, (resolve, reject) => { // For an auth session ticket we need the following: // 1. Length-prefixed GCTOKEN @@ -120,7 +120,7 @@ class SteamUserAppAuth extends SteamUserAccount { try { // We need to activate our ticket - await this.activateAuthSessionTickets(appid, buffer); + await this.activateAuthSessionTickets(appid, buffer, server_secret); resolve({sessionTicket: buffer}); } catch (err) { reject(err); @@ -171,7 +173,7 @@ class SteamUserAppAuth extends SteamUserAccount { }); } - activateAuthSessionTickets(appid, tickets, callback) { + activateAuthSessionTickets(appid, tickets, server_secret, callback) { if (!Array.isArray(tickets)) { tickets = [tickets]; } @@ -210,6 +212,7 @@ class SteamUserAppAuth extends SteamUserAccount { ticket_crc: StdLib.Hashing.crc32(ticket.authTicket), ticket: ticket.authTicket }; + if (server_secret) thisTicket.server_secret = Buffer.from(server_secret, 'hex'); // Check if this ticket is already active if (this._activeAuthTickets.find(tkt => tkt.steamid == thisTicket.steamid && tkt.ticket_crc == thisTicket.ticket_crc)) {
  2. A game I was playing was updated recently, and the backend server started to reject the ticket returned from createAuthSessionTicket. There is no change in the payload format, so I'm confused what could possibly cause this. The backend accepts the session ticket only if it was generated via the game's launcher, and the ticket stops working once the game is closed. If I login and request an new app ticket via node-steam-user after the game launcher generated its own ticket and BEFORE submitting to the game backend, then the backend server rejects both tickets. Simplified code example: // Flow 1: User login user.on("loggedOn", (details, parental) => { // user.setPersona(SteamUser.EPersonaState.Invisible); user.gamesPlayed([APP_ID]); }); // Prints "Ticket xxx validated by [I:1:1]: OK" when POST request to backend is made user.on('authTicketStatus', details => { logger.info(`Ticket ${details.ticketGcToken} validated by ${details.steamID.steam3()}: ${SteamUser.EAuthSessionResponse[details.authSessionResponse]}`); }); user.on('appLaunched', appID => { if (appID != APP_ID) { return; } resolve(user); // resolves the promise for Flow 1 }) user.logOn(creds); // ======== Flow 2 ========= // // Create app ticket and auth with backend const user = await loginSteamUserFlow1(); const {sessionTicket} = await user.createAuthSessionTicket(APP_ID); payload.token = sessionTicket.toString('hex').toUpperCase(); // Backend rejects this call with some vague error message "OAuth exception" const resp = await client.post(`${GAME_HOST}/session`, {json: payload}) Parsed tickets: // valid ticket, generated by game // sessionExternalIP is some random IP everytime // ownershipTicketExternalIP is not exactly my external IP, but it probably is a previously held IP // tokenGenerated doesnt seem to be anywhere near the current time { authTicket: <Buffer 14 00 00 00 b4 truncated... 2 more bytes>, gcToken: 'xxxx', tokenGenerated: 2023-06-02T02:22:25.000Z, sessionExternalIP: '101.218.247.224', clientConnectionTime: 2734877, clientConnectionCount: 4, version: 4, steamID: SteamID { universe: 1, type: 1, instance: 1, accountid: xxxx }, appID: 958260, ownershipTicketExternalIP: 'xxx.xxx.xxx.xx', ownershipTicketInternalIP: '192.168.1.101', ownershipFlags: 0, ownershipTicketGenerated: 2023-05-24T02:05:48.000Z, ownershipTicketExpires: 2023-06-14T02:05:48.000Z, licenses: [ 313233 ], dlc: [], signature: <Buffer 31 74 f0 b7 truncated... 78 more bytes>, isExpired: false, hasValidSignature: true, isValid: true } // generated by node-steam-user, rejected by backend { authTicket: <Buffer 14 00 00 00 b1 truncated ... 2 more bytes>, gcToken: 'xxxx', tokenGenerated: 2023-06-02T03:28:41.000Z, sessionExternalIP: 'xxx.xxx.xxx.xxx', clientConnectionTime: 101, clientConnectionCount: 1, version: 4, steamID: SteamID { universe: 1, type: 1, instance: 1, accountid: xxxxx }, appID: 958260, ownershipTicketExternalIP: 'xxx.xxx.xxx.xxx', ownershipTicketInternalIP: '186.129.57.126', ownershipFlags: 0, ownershipTicketGenerated: 2023-06-01T07:30:15.000Z, ownershipTicketExpires: 2023-06-22T07:30:15.000Z, licenses: [ 313233 ], dlc: [], signature: <Buffer 5c 7d 7c 8f truncated ... 78 more bytes>, isExpired: false, hasValidSignature: true, isValid: true } Reading the README for node-steam-appticket, it seems that some fields can be spoofed. Can we specify our own `sessionExternalIP` when requesting & activating app tickets? Can we specify our own `ownershipTicketInternalIP` as well? Why is the `ownershipTicketGenerated` by the game launcher so old compared to node-steam-user? Is it cached locally by the Steam client? Does `user.gamesPlayed([APP_ID])` do anything to affect the validity of the app ticket? How should I go about reversing the communication of the game <-> Steam ?
×
×
  • Create New...